@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.js CHANGED
@@ -1957,7 +1957,8 @@ var init_parsing = __esm({
1957
1957
  "wireframe",
1958
1958
  "tech-radar",
1959
1959
  "cycle",
1960
- "journey-map"
1960
+ "journey-map",
1961
+ "pyramid"
1961
1962
  ]);
1962
1963
  COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1963
1964
  OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
@@ -2083,7 +2084,8 @@ function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
2083
2084
  }
2084
2085
  }
2085
2086
  }
2086
- function validateTagGroupNames(tagGroups, pushWarning) {
2087
+ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
2088
+ const report = pushError ?? pushWarning;
2087
2089
  for (const group of tagGroups) {
2088
2090
  if (group.name.toLowerCase() === "none") {
2089
2091
  pushWarning(
@@ -2091,6 +2093,18 @@ function validateTagGroupNames(tagGroups, pushWarning) {
2091
2093
  `'none' is a reserved keyword and cannot be used as a tag group name`
2092
2094
  );
2093
2095
  }
2096
+ if (!VALID_TAG_IDENT_RE.test(group.name)) {
2097
+ report(
2098
+ group.lineNumber,
2099
+ `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
2100
+ );
2101
+ }
2102
+ if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
2103
+ report(
2104
+ group.lineNumber,
2105
+ `Tag group alias "${group.alias}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
2106
+ );
2107
+ }
2094
2108
  }
2095
2109
  }
2096
2110
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
@@ -2129,12 +2143,13 @@ function resolveActiveTagGroup(tagGroups, explicitActiveTag, programmaticOverrid
2129
2143
  function matchTagBlockHeading(trimmed) {
2130
2144
  return parseTagDeclaration(trimmed);
2131
2145
  }
2132
- var TAG_BLOCK_NOCOLON_RE;
2146
+ var TAG_BLOCK_NOCOLON_RE, VALID_TAG_IDENT_RE;
2133
2147
  var init_tag_groups = __esm({
2134
2148
  "src/utils/tag-groups.ts"() {
2135
2149
  "use strict";
2136
2150
  init_parsing();
2137
2151
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
2152
+ VALID_TAG_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
2138
2153
  }
2139
2154
  });
2140
2155
 
@@ -3070,7 +3085,6 @@ var init_participant_inference = __esm({
3070
3085
  { pattern: /^Admin$/i, type: "actor" },
3071
3086
  { pattern: /^User$/i, type: "actor" },
3072
3087
  { pattern: /^Customer$/i, type: "actor" },
3073
- { pattern: /^Client$/i, type: "actor" },
3074
3088
  { pattern: /^Agent$/i, type: "actor" },
3075
3089
  { pattern: /^Person$/i, type: "actor" },
3076
3090
  { pattern: /^Buyer$/i, type: "actor" },
@@ -3199,7 +3213,7 @@ function isSequenceSection(el) {
3199
3213
  function isSequenceNote(el) {
3200
3214
  return "kind" in el && el.kind === "note";
3201
3215
  }
3202
- function parseNoteLine(trimmed, participants, lastMsgFrom) {
3216
+ function parseNoteLine(trimmed, participants, participantIds, sortedParticipantsCache, lastMsgFrom) {
3203
3217
  const lower = trimmed.toLowerCase();
3204
3218
  if (!lower.startsWith("note")) return null;
3205
3219
  if (trimmed.length > 4 && trimmed[4] !== " ") return null;
@@ -3211,7 +3225,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3211
3225
  if (!lastMsgFrom) return { kind: "skip" };
3212
3226
  participantId = lastMsgFrom;
3213
3227
  }
3214
- if (participants.some((p) => p.id === participantId)) {
3228
+ if (participantIds.has(participantId)) {
3215
3229
  return { kind: "multi-head", position, participantId };
3216
3230
  }
3217
3231
  }
@@ -3230,11 +3244,15 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3230
3244
  }
3231
3245
  if (!afterPos) {
3232
3246
  if (!lastMsgFrom) return { kind: "skip" };
3233
- if (!participants.some((p) => p.id === lastMsgFrom))
3234
- return { kind: "skip" };
3247
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3235
3248
  return { kind: "multi-head", position, participantId: lastMsgFrom };
3236
3249
  }
3237
- const resolved = resolveParticipantAndText(afterPos, participants);
3250
+ const resolved = resolveParticipantAndText(
3251
+ afterPos,
3252
+ participants,
3253
+ participantIds,
3254
+ sortedParticipantsCache
3255
+ );
3238
3256
  if (resolved) {
3239
3257
  if (resolved.text) {
3240
3258
  return {
@@ -3253,8 +3271,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3253
3271
  }
3254
3272
  if (hadOf) return { kind: "skip" };
3255
3273
  if (!lastMsgFrom) return { kind: "skip" };
3256
- if (!participants.some((p) => p.id === lastMsgFrom))
3257
- return { kind: "skip" };
3274
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3258
3275
  return {
3259
3276
  kind: "single",
3260
3277
  position,
@@ -3263,8 +3280,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3263
3280
  };
3264
3281
  }
3265
3282
  if (!lastMsgFrom) return { kind: "skip" };
3266
- if (!participants.some((p) => p.id === lastMsgFrom))
3267
- return { kind: "skip" };
3283
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3268
3284
  return {
3269
3285
  kind: "single",
3270
3286
  position: "right",
@@ -3274,20 +3290,20 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3274
3290
  }
3275
3291
  return null;
3276
3292
  }
3277
- function resolveParticipantAndText(input, participants) {
3293
+ function resolveParticipantAndText(input, participants, participantIds, sortedParticipantsCache) {
3278
3294
  if (input.startsWith('"') || input.startsWith("'")) {
3279
3295
  const quote = input[0];
3280
3296
  const endQuote = input.indexOf(quote, 1);
3281
3297
  if (endQuote > 0) {
3282
3298
  const name = input.substring(1, endQuote);
3283
- if (participants.some((p) => p.id === name)) {
3299
+ if (participantIds.has(name)) {
3284
3300
  const text = input.substring(endQuote + 1).trim();
3285
3301
  return { participantId: name, text };
3286
3302
  }
3287
3303
  }
3288
3304
  return null;
3289
3305
  }
3290
- const sorted = [...participants].sort((a, b) => b.id.length - a.id.length);
3306
+ const sorted = sortedParticipantsCache;
3291
3307
  for (const p of sorted) {
3292
3308
  if (input.startsWith(p.id)) {
3293
3309
  const remaining = input.substring(p.id.length);
@@ -3348,6 +3364,18 @@ function parseSequenceDgmo(content) {
3348
3364
  break;
3349
3365
  }
3350
3366
  let activeGroup = null;
3367
+ const participantIds = /* @__PURE__ */ new Set();
3368
+ let sortedParticipantsCache = [];
3369
+ let sortedCacheDirty = true;
3370
+ const getSortedParticipants = () => {
3371
+ if (sortedCacheDirty) {
3372
+ sortedParticipantsCache = [...result.participants].sort(
3373
+ (a, b) => b.id.length - a.id.length
3374
+ );
3375
+ sortedCacheDirty = false;
3376
+ }
3377
+ return sortedParticipantsCache;
3378
+ };
3351
3379
  const participantGroupMap = /* @__PURE__ */ new Map();
3352
3380
  let currentTagGroup = null;
3353
3381
  const aliasMap = /* @__PURE__ */ new Map();
@@ -3593,7 +3621,9 @@ function parseSequenceDgmo(content) {
3593
3621
  const posMatch = remainder.match(/\bposition\s+(-?\d+)/i);
3594
3622
  const alias = akaMatch ? akaMatch[1].trim() : null;
3595
3623
  const position = posMatch ? parseInt(posMatch[1], 10) : void 0;
3596
- if (!result.participants.some((p) => p.id === id)) {
3624
+ if (!participantIds.has(id)) {
3625
+ participantIds.add(id);
3626
+ sortedCacheDirty = true;
3597
3627
  result.participants.push({
3598
3628
  id,
3599
3629
  label: alias || id,
@@ -3623,7 +3653,9 @@ function parseSequenceDgmo(content) {
3623
3653
  contentStarted = true;
3624
3654
  const id = posOnlyMatch[1];
3625
3655
  const position = parseInt(posOnlyMatch[2], 10);
3626
- if (!result.participants.some((p) => p.id === id)) {
3656
+ if (!participantIds.has(id)) {
3657
+ participantIds.add(id);
3658
+ sortedCacheDirty = true;
3627
3659
  result.participants.push({
3628
3660
  id,
3629
3661
  label: id,
@@ -3657,7 +3689,9 @@ function parseSequenceDgmo(content) {
3657
3689
  `'${id}(${color})' syntax is no longer supported \u2014 use 'tag:' groups for coloring`
3658
3690
  );
3659
3691
  contentStarted = true;
3660
- if (!result.participants.some((p) => p.id === id)) {
3692
+ if (!participantIds.has(id)) {
3693
+ participantIds.add(id);
3694
+ sortedCacheDirty = true;
3661
3695
  result.participants.push({
3662
3696
  id,
3663
3697
  label: id,
@@ -3686,7 +3720,8 @@ function parseSequenceDgmo(content) {
3686
3720
  if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
3687
3721
  contentStarted = true;
3688
3722
  const id = bareCore;
3689
- if (!result.participants.some((p) => p.id === id)) {
3723
+ if (!participantIds.has(id)) {
3724
+ participantIds.add(id);
3690
3725
  result.participants.push({
3691
3726
  id,
3692
3727
  label: id,
@@ -3748,7 +3783,9 @@ function parseSequenceDgmo(content) {
3748
3783
  };
3749
3784
  result.messages.push(msg);
3750
3785
  currentContainer().push(msg);
3751
- if (!result.participants.some((p) => p.id === from)) {
3786
+ if (!participantIds.has(from)) {
3787
+ participantIds.add(from);
3788
+ sortedCacheDirty = true;
3752
3789
  result.participants.push({
3753
3790
  id: from,
3754
3791
  label: from,
@@ -3756,7 +3793,9 @@ function parseSequenceDgmo(content) {
3756
3793
  lineNumber
3757
3794
  });
3758
3795
  }
3759
- if (!result.participants.some((p) => p.id === to)) {
3796
+ if (!participantIds.has(to)) {
3797
+ participantIds.add(to);
3798
+ sortedCacheDirty = true;
3760
3799
  result.participants.push({
3761
3800
  id: to,
3762
3801
  label: to,
@@ -3823,7 +3862,9 @@ function parseSequenceDgmo(content) {
3823
3862
  };
3824
3863
  result.messages.push(msg);
3825
3864
  currentContainer().push(msg);
3826
- if (!result.participants.some((p) => p.id === from)) {
3865
+ if (!participantIds.has(from)) {
3866
+ participantIds.add(from);
3867
+ sortedCacheDirty = true;
3827
3868
  result.participants.push({
3828
3869
  id: from,
3829
3870
  label: from,
@@ -3831,7 +3872,9 @@ function parseSequenceDgmo(content) {
3831
3872
  lineNumber
3832
3873
  });
3833
3874
  }
3834
- if (!result.participants.some((p) => p.id === to)) {
3875
+ if (!participantIds.has(to)) {
3876
+ participantIds.add(to);
3877
+ sortedCacheDirty = true;
3835
3878
  result.participants.push({
3836
3879
  id: to,
3837
3880
  label: to,
@@ -3933,6 +3976,8 @@ function parseSequenceDgmo(content) {
3933
3976
  const noteParsed = parseNoteLine(
3934
3977
  trimmed,
3935
3978
  result.participants,
3979
+ participantIds,
3980
+ getSortedParticipants(),
3936
3981
  lastMsgFrom
3937
3982
  );
3938
3983
  if (noteParsed) {
@@ -4037,7 +4082,7 @@ function parseSequenceDgmo(content) {
4037
4082
  entities.push({ metadata: g.metadata, lineNumber: g.lineNumber });
4038
4083
  }
4039
4084
  validateTagValues(entities, result.tagGroups, pushWarning, suggest);
4040
- validateTagGroupNames(result.tagGroups, pushWarning);
4085
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
4041
4086
  }
4042
4087
  return result;
4043
4088
  }
@@ -4060,7 +4105,7 @@ var init_parser = __esm({
4060
4105
  init_parsing();
4061
4106
  init_tag_groups();
4062
4107
  KNOWN_SEQ_OPTIONS = /* @__PURE__ */ new Set(["active-tag"]);
4063
- KNOWN_SEQ_BOOLEANS = /* @__PURE__ */ new Set(["activations", "collapse-notes"]);
4108
+ KNOWN_SEQ_BOOLEANS = /* @__PURE__ */ new Set(["activations"]);
4064
4109
  VALID_PARTICIPANT_TYPES = /* @__PURE__ */ new Set([
4065
4110
  "service",
4066
4111
  "database",
@@ -7321,7 +7366,7 @@ function buildFunnelOption(parsed, textColor, colors, bg, titleConfig) {
7321
7366
  bottom: 20,
7322
7367
  width: "60%",
7323
7368
  sort: "descending",
7324
- gap: 2,
7369
+ gap: 0,
7325
7370
  minSize: "8%"
7326
7371
  };
7327
7372
  return {
@@ -8436,7 +8481,7 @@ function parseOrg(content, palette) {
8436
8481
  };
8437
8482
  collectAll(result.roots);
8438
8483
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
8439
- validateTagGroupNames(result.tagGroups, pushWarning);
8484
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
8440
8485
  }
8441
8486
  if (result.roots.length === 0 && result.tagGroups.length === 0 && !result.error) {
8442
8487
  const diag = makeDgmoError(1, "No nodes found in org chart");
@@ -8769,7 +8814,11 @@ function parseKanban(content, palette) {
8769
8814
  if (result.columns.length === 0 && !result.error) {
8770
8815
  return fail(1, "No columns found. Use [Column Name] to define columns");
8771
8816
  }
8772
- validateTagGroupNames(result.tagGroups, warn);
8817
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
8818
+ const diag = makeDgmoError(line11, msg);
8819
+ result.diagnostics.push(diag);
8820
+ if (!result.error) result.error = formatDgmoError(diag);
8821
+ });
8773
8822
  return result;
8774
8823
  }
8775
8824
  function parseCardLine(trimmed, lineNumber, counter, aliasMap, _palette, _diagnostics) {
@@ -9895,7 +9944,7 @@ function parseSitemap(content, palette) {
9895
9944
  };
9896
9945
  collectAll(result.roots);
9897
9946
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
9898
- validateTagGroupNames(result.tagGroups, pushWarning);
9947
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
9899
9948
  }
9900
9949
  if (result.roots.length === 0 && result.tagGroups.length === 0 && !result.error) {
9901
9950
  const diag = makeDgmoError(1, "No pages found in sitemap");
@@ -10029,8 +10078,8 @@ function extractPipeMetadata(rest) {
10029
10078
  const tags = {};
10030
10079
  let clean = rest;
10031
10080
  let match;
10032
- const re = new RegExp(PIPE_META_RE.source, "g");
10033
- while ((match = re.exec(rest)) !== null) {
10081
+ PIPE_META_RE.lastIndex = 0;
10082
+ while ((match = PIPE_META_RE.exec(rest)) !== null) {
10034
10083
  tags[match[1].trim()] = match[2].trim();
10035
10084
  clean = clean.replace(match[0], "");
10036
10085
  }
@@ -10532,7 +10581,7 @@ function parseInfra(content) {
10532
10581
  }
10533
10582
  }
10534
10583
  }
10535
- validateTagGroupNames(result.tagGroups, warn);
10584
+ validateTagGroupNames(result.tagGroups, warn, setError);
10536
10585
  return result;
10537
10586
  }
10538
10587
  function extractSymbols4(docText) {
@@ -11473,7 +11522,11 @@ function parseGantt(content, palette) {
11473
11522
  warn(0, "sort tag has no effect \u2014 no tag groups defined.");
11474
11523
  result.options.sort = "default";
11475
11524
  }
11476
- validateTagGroupNames(result.tagGroups, warn);
11525
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
11526
+ const diag = makeDgmoError(line11, msg);
11527
+ diagnostics.push(diag);
11528
+ if (!result.error) result.error = formatDgmoError(diag);
11529
+ });
11477
11530
  const hasSprintOption = result.options.sprintLength !== null || result.options.sprintNumber !== null || result.options.sprintStart !== null;
11478
11531
  const hasSprintUnit = hasSprintDurationUnit(result.nodes);
11479
11532
  if (hasSprintOption) {
@@ -11737,6 +11790,7 @@ function parseBoxesAndLines(content) {
11737
11790
  const groupLabels = /* @__PURE__ */ new Set();
11738
11791
  let lastNodeLabel = null;
11739
11792
  let lastSourceIsGroup = false;
11793
+ let lastNodeIndent = 0;
11740
11794
  let descState = null;
11741
11795
  function flushDescription() {
11742
11796
  if (descState && descState.lines.length > 0) {
@@ -12034,7 +12088,8 @@ function parseBoxesAndLines(content) {
12034
12088
  if (trimmed.startsWith("->") || /^-[^>].*->/.test(trimmed)) {
12035
12089
  const gs2 = currentGroupState();
12036
12090
  const inGroup = gs2 && indent > gs2.indent;
12037
- if (inGroup) {
12091
+ const indentedUnderNode = lastNodeLabel && !lastSourceIsGroup && indent > lastNodeIndent;
12092
+ if (inGroup && !indentedUnderNode) {
12038
12093
  const sourcePrefix = `[${gs2.group.label}]`;
12039
12094
  edgeText = `${sourcePrefix} ${trimmed}`;
12040
12095
  } else if (lastNodeLabel) {
@@ -12074,6 +12129,7 @@ function parseBoxesAndLines(content) {
12074
12129
  }
12075
12130
  lastNodeLabel = node.label;
12076
12131
  lastSourceIsGroup = false;
12132
+ lastNodeIndent = indent;
12077
12133
  const gs = currentGroupState();
12078
12134
  const isGroupChild = gs && indent > gs.indent;
12079
12135
  if (nodeLabels.has(node.label)) {
@@ -12142,7 +12198,11 @@ function parseBoxesAndLines(content) {
12142
12198
  if (result.tagGroups.length > 0) {
12143
12199
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
12144
12200
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
12145
- validateTagGroupNames(result.tagGroups, pushWarning);
12201
+ validateTagGroupNames(result.tagGroups, pushWarning, (line11, msg) => {
12202
+ const diag = makeDgmoError(line11, msg);
12203
+ result.diagnostics.push(diag);
12204
+ if (!result.error) result.error = diag.message;
12205
+ });
12146
12206
  }
12147
12207
  return result;
12148
12208
  }
@@ -13118,7 +13178,9 @@ function parseWireframe(content) {
13118
13178
  }
13119
13179
  }
13120
13180
  }
13121
- validateTagGroupNames(tagGroups, pushWarning);
13181
+ validateTagGroupNames(tagGroups, pushWarning, (line11, msg) => {
13182
+ diagnostics.push(makeDgmoError(line11, msg));
13183
+ });
13122
13184
  const error = diagnostics.find((d) => d.severity === "error") ? formatDgmoError(diagnostics.find((d) => d.severity === "error")) : null;
13123
13185
  return {
13124
13186
  title,
@@ -14046,7 +14108,11 @@ function parseJourneyMap(content, palette) {
14046
14108
  if (result.phases.length === 0 && result.steps.length === 0 && !result.error) {
14047
14109
  return fail(1, "No phases or steps found");
14048
14110
  }
14049
- validateTagGroupNames(result.tagGroups, warn);
14111
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
14112
+ const diag = makeDgmoError(line11, msg);
14113
+ result.diagnostics.push(diag);
14114
+ if (!result.error) result.error = formatDgmoError(diag);
14115
+ });
14050
14116
  return result;
14051
14117
  }
14052
14118
  function parseStepLine(trimmed, lineNumber, counter, aliasMap, warn) {
@@ -14158,12 +14224,543 @@ var init_parser15 = __esm({
14158
14224
  }
14159
14225
  });
14160
14226
 
14227
+ // src/pyramid/parser.ts
14228
+ var parser_exports16 = {};
14229
+ __export(parser_exports16, {
14230
+ parsePyramid: () => parsePyramid
14231
+ });
14232
+ function parsePyramid(content) {
14233
+ const result = {
14234
+ type: "pyramid",
14235
+ title: "",
14236
+ titleLineNumber: 0,
14237
+ layers: [],
14238
+ inverted: false,
14239
+ options: {},
14240
+ diagnostics: [],
14241
+ error: null
14242
+ };
14243
+ const lines = content.split("\n");
14244
+ let headerParsed = false;
14245
+ let currentLayer = null;
14246
+ const fail = (line11, message) => {
14247
+ const diag = makeDgmoError(line11, message);
14248
+ result.diagnostics.push(diag);
14249
+ result.error = formatDgmoError(diag);
14250
+ return result;
14251
+ };
14252
+ const warn = (line11, message, severity = "warning") => {
14253
+ result.diagnostics.push(makeDgmoError(line11, message, severity));
14254
+ };
14255
+ const flushLayer = () => {
14256
+ if (currentLayer) {
14257
+ result.layers.push(currentLayer);
14258
+ currentLayer = null;
14259
+ }
14260
+ };
14261
+ for (let i = 0; i < lines.length; i++) {
14262
+ const lineNum = i + 1;
14263
+ const raw = lines[i];
14264
+ const trimmed = raw.trim();
14265
+ if (!trimmed || trimmed.startsWith("//")) continue;
14266
+ const indent = measureIndent(raw);
14267
+ if (!headerParsed) {
14268
+ const firstLineResult = parseFirstLine(trimmed);
14269
+ if (firstLineResult && firstLineResult.chartType === "pyramid") {
14270
+ result.title = firstLineResult.title ?? "";
14271
+ result.titleLineNumber = lineNum;
14272
+ headerParsed = true;
14273
+ continue;
14274
+ }
14275
+ return fail(lineNum, 'Expected "pyramid [Title]" as the first line.');
14276
+ }
14277
+ if (indent === 0 && trimmed.toLowerCase() === "inverted") {
14278
+ result.inverted = true;
14279
+ continue;
14280
+ }
14281
+ if (indent === 0) {
14282
+ flushLayer();
14283
+ const pipeIdx = trimmed.indexOf("|");
14284
+ let label;
14285
+ const description = [];
14286
+ let color;
14287
+ let restMeta = {};
14288
+ if (pipeIdx < 0) {
14289
+ label = trimmed;
14290
+ } else {
14291
+ label = trimmed.substring(0, pipeIdx).trim();
14292
+ const after = trimmed.substring(pipeIdx + 1).trim();
14293
+ if (!after) {
14294
+ } else if (KEY_VALUE_PREFIX_RE.test(after)) {
14295
+ const metadata = parsePipeMetadata([label, after]);
14296
+ color = metadata["color"];
14297
+ const descFromPipe = metadata["description"];
14298
+ if (descFromPipe) description.push(descFromPipe);
14299
+ restMeta = { ...metadata };
14300
+ delete restMeta["color"];
14301
+ delete restMeta["description"];
14302
+ } else {
14303
+ description.push(after);
14304
+ }
14305
+ }
14306
+ if (!label) {
14307
+ warn(lineNum, "Empty layer label.");
14308
+ continue;
14309
+ }
14310
+ currentLayer = {
14311
+ label,
14312
+ lineNumber: lineNum,
14313
+ color,
14314
+ description,
14315
+ metadata: restMeta
14316
+ };
14317
+ continue;
14318
+ }
14319
+ if (!currentLayer) {
14320
+ warn(lineNum, `Unexpected indented line: "${trimmed}".`);
14321
+ continue;
14322
+ }
14323
+ const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
14324
+ currentLayer.description.push(descLine);
14325
+ }
14326
+ flushLayer();
14327
+ if (result.layers.length < 2) {
14328
+ return fail(
14329
+ result.titleLineNumber || 1,
14330
+ "pyramid requires at least 2 layers."
14331
+ );
14332
+ }
14333
+ return result;
14334
+ }
14335
+ var KEY_VALUE_PREFIX_RE;
14336
+ var init_parser16 = __esm({
14337
+ "src/pyramid/parser.ts"() {
14338
+ "use strict";
14339
+ init_diagnostics();
14340
+ init_parsing();
14341
+ KEY_VALUE_PREFIX_RE = /^\s*[A-Za-z][A-Za-z0-9_-]*\s*:/;
14342
+ }
14343
+ });
14344
+
14345
+ // src/chart-types.ts
14346
+ var chartTypes;
14347
+ var init_chart_types = __esm({
14348
+ "src/chart-types.ts"() {
14349
+ "use strict";
14350
+ chartTypes = [
14351
+ // ── Tier 1 — Narrative / architecture diagrams ────────────
14352
+ {
14353
+ id: "journey-map",
14354
+ description: "User experience flow with emotion scores, phases, and annotations",
14355
+ triggers: [
14356
+ "user journey",
14357
+ "customer journey",
14358
+ "customer experience",
14359
+ "customer goes through",
14360
+ "user goes through",
14361
+ "customer path",
14362
+ "customer touchpoints",
14363
+ "cx flow",
14364
+ "ux journey",
14365
+ "onboarding flow",
14366
+ "persona journey",
14367
+ "empathy map",
14368
+ "service blueprint"
14369
+ ]
14370
+ },
14371
+ {
14372
+ id: "c4",
14373
+ description: "System architecture (context, container, component, deployment)",
14374
+ triggers: [
14375
+ "c4 diagram",
14376
+ "system context",
14377
+ "container diagram",
14378
+ "component diagram",
14379
+ "architecture overview",
14380
+ "software architecture"
14381
+ ]
14382
+ },
14383
+ {
14384
+ id: "er",
14385
+ description: "Database schemas and relationships",
14386
+ triggers: [
14387
+ "database schema",
14388
+ "er diagram",
14389
+ "entity relationship",
14390
+ "data model",
14391
+ "tables and relationships",
14392
+ "foreign keys"
14393
+ ]
14394
+ },
14395
+ {
14396
+ id: "class",
14397
+ description: "UML class hierarchies",
14398
+ triggers: [
14399
+ "uml class",
14400
+ "class hierarchy",
14401
+ "class diagram",
14402
+ "inheritance tree",
14403
+ "oop structure"
14404
+ ]
14405
+ },
14406
+ {
14407
+ id: "sequence",
14408
+ description: "Message / interaction flows",
14409
+ triggers: [
14410
+ "sequence diagram",
14411
+ "message flow",
14412
+ "api call flow",
14413
+ "request lifecycle",
14414
+ "interaction diagram",
14415
+ "call sequence"
14416
+ ],
14417
+ fallback: true
14418
+ },
14419
+ {
14420
+ id: "state",
14421
+ description: "State machine / lifecycle transitions",
14422
+ triggers: [
14423
+ "state diagram",
14424
+ "state machine",
14425
+ "state transitions",
14426
+ "lifecycle diagram",
14427
+ "status transitions"
14428
+ ]
14429
+ },
14430
+ {
14431
+ id: "infra",
14432
+ description: "Infrastructure traffic flow with RPS computation",
14433
+ triggers: [
14434
+ "infrastructure diagram",
14435
+ "traffic flow",
14436
+ "request path",
14437
+ "rps",
14438
+ "capacity planning",
14439
+ "network topology"
14440
+ ]
14441
+ },
14442
+ {
14443
+ id: "gantt",
14444
+ description: "Project scheduling with task dependencies and milestones",
14445
+ triggers: [
14446
+ "gantt chart",
14447
+ "project schedule",
14448
+ "sprint plan",
14449
+ "project timeline",
14450
+ "task dependencies",
14451
+ "project milestones"
14452
+ ]
14453
+ },
14454
+ // ── Tier 2 — Specialized structural diagrams ──────────────
14455
+ {
14456
+ id: "timeline",
14457
+ description: "Events, eras, and date ranges",
14458
+ triggers: [
14459
+ "event timeline",
14460
+ "historical timeline",
14461
+ "era chart",
14462
+ "period chart",
14463
+ "project history"
14464
+ ]
14465
+ },
14466
+ {
14467
+ id: "org",
14468
+ description: "Reporting hierarchy",
14469
+ triggers: [
14470
+ "org chart",
14471
+ "organization chart",
14472
+ "reporting structure",
14473
+ "hierarchy chart",
14474
+ "team structure"
14475
+ ]
14476
+ },
14477
+ {
14478
+ id: "sitemap",
14479
+ description: "Site / app navigation structure",
14480
+ triggers: [
14481
+ "sitemap",
14482
+ "site structure",
14483
+ "page hierarchy",
14484
+ "navigation structure",
14485
+ "app navigation"
14486
+ ]
14487
+ },
14488
+ {
14489
+ id: "kanban",
14490
+ description: "Task board columns",
14491
+ triggers: [
14492
+ "kanban board",
14493
+ "task board",
14494
+ "workflow columns",
14495
+ "todo doing done",
14496
+ "agile board"
14497
+ ]
14498
+ },
14499
+ {
14500
+ id: "tech-radar",
14501
+ description: "Technology adoption quadrants (adopt/trial/assess/hold)",
14502
+ triggers: [
14503
+ "tech radar",
14504
+ "technology radar",
14505
+ "tech adoption",
14506
+ "adopt trial assess hold",
14507
+ "tech choices"
14508
+ ]
14509
+ },
14510
+ {
14511
+ id: "mindmap",
14512
+ description: "Radial hierarchy of ideas branching from a central topic",
14513
+ triggers: [
14514
+ "mind map",
14515
+ "brainstorm diagram",
14516
+ "concept map",
14517
+ "idea tree",
14518
+ "radial ideas"
14519
+ ]
14520
+ },
14521
+ {
14522
+ id: "wireframe",
14523
+ description: "Low-fidelity UI layout with panels, controls, and annotations",
14524
+ triggers: [
14525
+ "wireframe",
14526
+ "ui mockup",
14527
+ "screen layout",
14528
+ "page layout",
14529
+ "low-fidelity mockup"
14530
+ ]
14531
+ },
14532
+ {
14533
+ id: "cycle",
14534
+ description: "Cyclical process visualization (PDCA, OODA, DevOps loops)",
14535
+ triggers: [
14536
+ "pdca cycle",
14537
+ "ooda loop",
14538
+ "feedback loop",
14539
+ "cyclical process",
14540
+ "devops loop",
14541
+ "continuous loop"
14542
+ ]
14543
+ },
14544
+ {
14545
+ id: "pyramid",
14546
+ description: "Stacked hierarchy of layers with descriptions (Maslow, DIKW)",
14547
+ triggers: [
14548
+ "pyramid diagram",
14549
+ "layered hierarchy",
14550
+ "maslow hierarchy",
14551
+ "dikw pyramid",
14552
+ "layered model"
14553
+ ]
14554
+ },
14555
+ // ── Tier 3 — Specialized analytical charts ────────────────
14556
+ {
14557
+ id: "quadrant",
14558
+ description: "2x2 positioning matrix",
14559
+ triggers: [
14560
+ "2x2 matrix",
14561
+ "priority matrix",
14562
+ "quadrant chart",
14563
+ "impact effort matrix",
14564
+ "positioning matrix"
14565
+ ]
14566
+ },
14567
+ {
14568
+ id: "venn",
14569
+ description: "Set overlaps",
14570
+ triggers: [
14571
+ "venn diagram",
14572
+ "set overlap",
14573
+ "intersection of",
14574
+ "shared traits",
14575
+ "overlapping circles"
14576
+ ]
14577
+ },
14578
+ {
14579
+ id: "funnel",
14580
+ description: "Conversion pipeline",
14581
+ triggers: [
14582
+ "conversion funnel",
14583
+ "sales funnel",
14584
+ "user funnel",
14585
+ "pipeline stages",
14586
+ "drop-off funnel"
14587
+ ]
14588
+ },
14589
+ {
14590
+ id: "slope",
14591
+ description: "Change between two periods",
14592
+ triggers: [
14593
+ "slope chart",
14594
+ "before and after",
14595
+ "two-period change",
14596
+ "delta chart",
14597
+ "shift comparison"
14598
+ ]
14599
+ },
14600
+ {
14601
+ id: "sankey",
14602
+ description: "Flow / allocation visualization",
14603
+ triggers: [
14604
+ "sankey diagram",
14605
+ "flow allocation",
14606
+ "budget flow",
14607
+ "energy flow",
14608
+ "traffic allocation"
14609
+ ]
14610
+ },
14611
+ {
14612
+ id: "chord",
14613
+ description: "Circular flow relationships",
14614
+ triggers: [
14615
+ "chord diagram",
14616
+ "circular flow",
14617
+ "relationship wheel",
14618
+ "team connections"
14619
+ ]
14620
+ },
14621
+ {
14622
+ id: "arc",
14623
+ description: "Network relationships",
14624
+ triggers: [
14625
+ "arc diagram",
14626
+ "relationship chart",
14627
+ "connection arcs",
14628
+ "network arcs"
14629
+ ]
14630
+ },
14631
+ {
14632
+ id: "wordcloud",
14633
+ description: "Term frequency visualization",
14634
+ triggers: [
14635
+ "word cloud",
14636
+ "tag cloud",
14637
+ "term frequency",
14638
+ "keyword frequency"
14639
+ ]
14640
+ },
14641
+ {
14642
+ id: "heatmap",
14643
+ description: "Matrix intensity visualization",
14644
+ triggers: [
14645
+ "heatmap",
14646
+ "intensity matrix",
14647
+ "activity heatmap",
14648
+ "correlation matrix"
14649
+ ]
14650
+ },
14651
+ {
14652
+ id: "function",
14653
+ description: "Mathematical expressions",
14654
+ triggers: [
14655
+ "function plot",
14656
+ "mathematical plot",
14657
+ "equation chart",
14658
+ "graph y=f(x)"
14659
+ ]
14660
+ },
14661
+ // ── Tier 4 — General-purpose data charts ──────────────────
14662
+ {
14663
+ id: "bar",
14664
+ description: "Categorical comparisons",
14665
+ triggers: ["bar chart", "categorical comparison", "bar graph"],
14666
+ fallback: true
14667
+ },
14668
+ {
14669
+ id: "line",
14670
+ description: "Trends over time",
14671
+ triggers: ["line chart", "trend over time", "time series"],
14672
+ fallback: true
14673
+ },
14674
+ {
14675
+ id: "multi-line",
14676
+ description: "Multiple series trends over time",
14677
+ triggers: [
14678
+ "multi-line chart",
14679
+ "multiple trends",
14680
+ "multiple series over time"
14681
+ ]
14682
+ },
14683
+ {
14684
+ id: "area",
14685
+ description: "Filled line chart",
14686
+ triggers: ["area chart", "filled line", "cumulative trend"]
14687
+ },
14688
+ {
14689
+ id: "pie",
14690
+ description: "Part-to-whole proportions",
14691
+ triggers: ["pie chart", "part to whole", "percentage breakdown"]
14692
+ },
14693
+ {
14694
+ id: "doughnut",
14695
+ description: "Ring-style pie chart",
14696
+ triggers: ["doughnut chart", "donut chart", "ring chart"]
14697
+ },
14698
+ {
14699
+ id: "radar",
14700
+ description: "Multi-dimensional metrics",
14701
+ triggers: ["radar chart", "spider chart", "multi-dimensional metrics"]
14702
+ },
14703
+ {
14704
+ id: "polar-area",
14705
+ description: "Radial bar chart",
14706
+ triggers: ["polar area", "radial bar chart"]
14707
+ },
14708
+ {
14709
+ id: "bar-stacked",
14710
+ description: "Multi-series categorical",
14711
+ triggers: [
14712
+ "stacked bar",
14713
+ "stacked bar chart",
14714
+ "multi-series bar",
14715
+ "composite bar"
14716
+ ]
14717
+ },
14718
+ {
14719
+ id: "scatter",
14720
+ description: "2D data points or bubble chart",
14721
+ triggers: [
14722
+ "scatter plot",
14723
+ "correlation plot",
14724
+ "bubble chart",
14725
+ "2d data points"
14726
+ ]
14727
+ },
14728
+ // ── Tier 5 — Generic catch-alls (listed last on purpose) ──
14729
+ {
14730
+ id: "flowchart",
14731
+ description: "Decision trees and process flows",
14732
+ triggers: [
14733
+ "flowchart",
14734
+ "decision tree",
14735
+ "if-then diagram",
14736
+ "process flow with decisions"
14737
+ ],
14738
+ fallback: true
14739
+ },
14740
+ {
14741
+ id: "boxes-and-lines",
14742
+ description: "General-purpose node-edge diagrams with groups and tags",
14743
+ triggers: [
14744
+ "boxes and lines",
14745
+ "nodes and edges",
14746
+ "generic diagram",
14747
+ "general-purpose network"
14748
+ ],
14749
+ fallback: true
14750
+ }
14751
+ ];
14752
+ }
14753
+ });
14754
+
14161
14755
  // src/dgmo-router.ts
14162
14756
  var dgmo_router_exports = {};
14163
14757
  __export(dgmo_router_exports, {
14758
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
14759
+ chartTypeParsers: () => chartTypeParsers,
14164
14760
  getAllChartTypes: () => getAllChartTypes,
14165
14761
  getRenderCategory: () => getRenderCategory,
14166
14762
  isExtendedChartType: () => isExtendedChartType,
14763
+ knownChartTypeIds: () => knownChartTypeIds,
14167
14764
  looksLikeC4: () => looksLikeC4,
14168
14765
  looksLikeGantt: () => looksLikeGantt,
14169
14766
  parseDgmo: () => parseDgmo,
@@ -14222,7 +14819,7 @@ function isExtendedChartType(chartType) {
14222
14819
  return EXTENDED_CHART_TYPES.has(chartType.toLowerCase());
14223
14820
  }
14224
14821
  function getAllChartTypes() {
14225
- return [...DATA_CHART_TYPES, ...VISUALIZATION_TYPES, ...DIAGRAM_TYPES];
14822
+ return chartTypes.map((c) => c.id);
14226
14823
  }
14227
14824
  function parseDgmo(content) {
14228
14825
  const chartType = parseDgmoChartType(content);
@@ -14230,32 +14827,25 @@ function parseDgmo(content) {
14230
14827
  const colonDiag = detectColonChartType(content);
14231
14828
  if (colonDiag) {
14232
14829
  const fallback = parseVisualization(content).diagnostics;
14233
- return { diagnostics: [colonDiag, ...fallback] };
14830
+ return { diagnostics: [colonDiag, ...fallback], chartType: null };
14234
14831
  }
14235
- return { diagnostics: parseVisualization(content).diagnostics };
14236
- }
14237
- const directParser = PARSE_DISPATCH.get(chartType);
14238
- if (directParser) {
14239
- const result2 = directParser(content);
14240
- return {
14241
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14242
- };
14243
- }
14244
- if (STANDARD_CHART_TYPES2.has(chartType)) {
14245
- const result2 = parseChart(content);
14246
14832
  return {
14247
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14833
+ diagnostics: parseVisualization(content).diagnostics,
14834
+ chartType: null
14248
14835
  };
14249
14836
  }
14250
- if (ECHART_TYPES.has(chartType)) {
14251
- const result2 = parseExtendedChart(content);
14837
+ const parser = PARSER_BY_ID.get(chartType);
14838
+ if (parser) {
14839
+ const result2 = parser(content);
14252
14840
  return {
14253
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14841
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14842
+ chartType
14254
14843
  };
14255
14844
  }
14256
14845
  const result = parseVisualization(content);
14257
14846
  return {
14258
- diagnostics: [...result.diagnostics, ...detectEmptyContent(content)]
14847
+ diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
14848
+ chartType
14259
14849
  };
14260
14850
  }
14261
14851
  function detectColonChartType(content) {
@@ -14298,7 +14888,7 @@ function detectEmptyContent(content) {
14298
14888
  }
14299
14889
  return [];
14300
14890
  }
14301
- var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, STANDARD_CHART_TYPES2, ECHART_TYPES, PARSE_DISPATCH, ALL_KNOWN_TYPES;
14891
+ 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;
14302
14892
  var init_dgmo_router = __esm({
14303
14893
  "src/dgmo-router.ts"() {
14304
14894
  "use strict";
@@ -14322,8 +14912,10 @@ var init_dgmo_router = __esm({
14322
14912
  init_parser13();
14323
14913
  init_parser14();
14324
14914
  init_parser15();
14915
+ init_parser16();
14325
14916
  init_parsing();
14326
14917
  init_diagnostics();
14918
+ init_chart_types();
14327
14919
  GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
14328
14920
  GANTT_DATE_RE = /^\d{4}-\d{2}-\d{2}(?:\s\d{2}:\d{2})?\s+/;
14329
14921
  C4_TYPE_RE = /\bis\s+an?\s+(person|system|container|component)\b/i;
@@ -14352,7 +14944,8 @@ var init_dgmo_router = __esm({
14352
14944
  "venn",
14353
14945
  "quadrant",
14354
14946
  "tech-radar",
14355
- "cycle"
14947
+ "cycle",
14948
+ "pyramid"
14356
14949
  ]);
14357
14950
  DIAGRAM_TYPES = /* @__PURE__ */ new Set([
14358
14951
  "sequence",
@@ -14379,49 +14972,57 @@ var init_dgmo_router = __esm({
14379
14972
  "heatmap",
14380
14973
  "funnel"
14381
14974
  ]);
14382
- STANDARD_CHART_TYPES2 = /* @__PURE__ */ new Set([
14383
- "bar",
14384
- "line",
14385
- "multi-line",
14386
- "area",
14387
- "pie",
14388
- "doughnut",
14389
- "radar",
14390
- "polar-area",
14391
- "bar-stacked"
14392
- ]);
14393
- ECHART_TYPES = /* @__PURE__ */ new Set([
14394
- "scatter",
14395
- "sankey",
14396
- "chord",
14397
- "function",
14398
- "heatmap",
14399
- "funnel"
14400
- ]);
14401
- PARSE_DISPATCH = /* @__PURE__ */ new Map([
14402
- ["sequence", (c) => parseSequenceDgmo(c)],
14403
- ["flowchart", (c) => parseFlowchart(c)],
14404
- ["class", (c) => parseClassDiagram(c)],
14405
- ["er", (c) => parseERDiagram(c)],
14406
- ["org", (c) => parseOrg(c)],
14407
- ["kanban", (c) => parseKanban(c)],
14408
- ["c4", (c) => parseC4(c)],
14409
- ["state", (c) => parseState(c)],
14410
- ["sitemap", (c) => parseSitemap(c)],
14411
- ["infra", (c) => parseInfra(c)],
14412
- ["gantt", (c) => parseGantt(c)],
14413
- ["boxes-and-lines", (c) => parseBoxesAndLines(c)],
14414
- ["mindmap", (c) => parseMindmap(c)],
14415
- ["wireframe", (c) => parseWireframe(c)],
14416
- ["tech-radar", (c) => parseTechRadar(c)],
14417
- ["cycle", (c) => parseCycle(c)],
14418
- ["journey-map", (c) => parseJourneyMap(c)]
14419
- ]);
14420
- ALL_KNOWN_TYPES = /* @__PURE__ */ new Set([
14421
- ...DATA_CHART_TYPES,
14422
- ...VISUALIZATION_TYPES,
14423
- ...DIAGRAM_TYPES
14424
- ]);
14975
+ CHART_TYPE_DESCRIPTIONS = Object.fromEntries(chartTypes.map((c) => [c.id, c.description]));
14976
+ chartTypeParsers = [
14977
+ // Structured diagrams (direct parsers)
14978
+ ["sequence", parseSequenceDgmo],
14979
+ ["flowchart", parseFlowchart],
14980
+ ["class", parseClassDiagram],
14981
+ ["er", parseERDiagram],
14982
+ ["state", parseState],
14983
+ ["org", parseOrg],
14984
+ ["kanban", parseKanban],
14985
+ ["c4", parseC4],
14986
+ ["sitemap", parseSitemap],
14987
+ ["infra", parseInfra],
14988
+ ["gantt", parseGantt],
14989
+ ["boxes-and-lines", parseBoxesAndLines],
14990
+ ["mindmap", parseMindmap],
14991
+ ["wireframe", parseWireframe],
14992
+ ["tech-radar", parseTechRadar],
14993
+ ["cycle", parseCycle],
14994
+ ["journey-map", parseJourneyMap],
14995
+ ["pyramid", parsePyramid],
14996
+ // Standard ECharts charts (parseChart)
14997
+ ["bar", parseChart],
14998
+ ["line", parseChart],
14999
+ ["multi-line", parseChart],
15000
+ ["area", parseChart],
15001
+ ["pie", parseChart],
15002
+ ["doughnut", parseChart],
15003
+ ["radar", parseChart],
15004
+ ["polar-area", parseChart],
15005
+ ["bar-stacked", parseChart],
15006
+ // Extended ECharts charts (parseExtendedChart)
15007
+ ["scatter", parseExtendedChart],
15008
+ ["sankey", parseExtendedChart],
15009
+ ["chord", parseExtendedChart],
15010
+ ["function", parseExtendedChart],
15011
+ ["heatmap", parseExtendedChart],
15012
+ ["funnel", parseExtendedChart],
15013
+ // D3 visualizations (parseVisualization)
15014
+ ["slope", parseVisualization],
15015
+ ["wordcloud", parseVisualization],
15016
+ ["arc", parseVisualization],
15017
+ ["timeline", parseVisualization],
15018
+ ["venn", parseVisualization],
15019
+ ["quadrant", parseVisualization]
15020
+ ];
15021
+ knownChartTypeIds = chartTypeParsers.map(
15022
+ ([id]) => id
15023
+ );
15024
+ PARSER_BY_ID = new Map(chartTypeParsers);
15025
+ ALL_KNOWN_TYPES = new Set(knownChartTypeIds);
14425
15026
  }
14426
15027
  });
14427
15028
 
@@ -15224,7 +15825,8 @@ var init_layout = __esm({
15224
15825
  // src/org/collapse.ts
15225
15826
  var collapse_exports = {};
15226
15827
  __export(collapse_exports, {
15227
- collapseOrgTree: () => collapseOrgTree
15828
+ collapseOrgTree: () => collapseOrgTree,
15829
+ focusOrgTree: () => focusOrgTree
15228
15830
  });
15229
15831
  function cloneNode(node) {
15230
15832
  return {
@@ -15279,6 +15881,49 @@ function collapseOrgTree(original, collapsedIds) {
15279
15881
  hiddenCounts
15280
15882
  };
15281
15883
  }
15884
+ function findNodeWithPath(nodes, targetId, path) {
15885
+ for (const node of nodes) {
15886
+ if (node.id === targetId) {
15887
+ return { node, path };
15888
+ }
15889
+ const result = findNodeWithPath(node.children, targetId, [
15890
+ ...path,
15891
+ {
15892
+ id: node.id,
15893
+ label: node.label,
15894
+ lineNumber: node.lineNumber,
15895
+ color: node.color,
15896
+ metadata: { ...node.metadata },
15897
+ isContainer: node.isContainer
15898
+ }
15899
+ ]);
15900
+ if (result) return result;
15901
+ }
15902
+ return null;
15903
+ }
15904
+ function focusOrgTree(original, focusNodeId) {
15905
+ const found = findNodeWithPath(original.roots, focusNodeId, []);
15906
+ if (!found) return null;
15907
+ const isRoot = original.roots.some((r) => r.id === focusNodeId);
15908
+ if (isRoot) {
15909
+ return {
15910
+ parsed: {
15911
+ ...original,
15912
+ roots: [cloneNode(found.node)]
15913
+ },
15914
+ ancestorPath: []
15915
+ };
15916
+ }
15917
+ const cloned = cloneNode(found.node);
15918
+ cloned.parentId = null;
15919
+ return {
15920
+ parsed: {
15921
+ ...original,
15922
+ roots: [cloned]
15923
+ },
15924
+ ancestorPath: found.path
15925
+ };
15926
+ }
15282
15927
  var init_collapse = __esm({
15283
15928
  "src/org/collapse.ts"() {
15284
15929
  "use strict";
@@ -15338,7 +15983,7 @@ function containerFill(palette, isDark, nodeColor2) {
15338
15983
  function containerStroke(palette, nodeColor2) {
15339
15984
  return nodeColor2 ?? palette.textMuted;
15340
15985
  }
15341
- function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes) {
15986
+ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes, ancestorPath) {
15342
15987
  d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
15343
15988
  const width = exportDims?.width ?? container.clientWidth;
15344
15989
  const height = exportDims?.height ?? container.clientHeight;
@@ -15351,8 +15996,10 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15351
15996
  const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
15352
15997
  const fixedTitle = !exportDims && !!parsed.title;
15353
15998
  const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
15999
+ const hasAncestorTrail = !exportDims && ancestorPath && ancestorPath.length > 0;
16000
+ const ancestorTrailHeight = hasAncestorTrail ? ancestorPath.length * ANCESTOR_ROW_HEIGHT + ANCESTOR_TRAIL_BOTTOM_GAP : 0;
15354
16001
  const diagramW = layout.width;
15355
- let diagramH = layout.height + (fixedTitle ? 0 : titleOffset);
16002
+ let diagramH = layout.height + (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15356
16003
  if (fixedLegend) {
15357
16004
  diagramH -= layoutLegendShift;
15358
16005
  }
@@ -15384,11 +16031,13 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15384
16031
  }
15385
16032
  }
15386
16033
  }
15387
- const contentG = mainG.append("g").attr("transform", `translate(0, ${fixedTitle ? 0 : titleOffset})`);
16034
+ const contentYShift = (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
16035
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
15388
16036
  const displayNames = /* @__PURE__ */ new Map();
15389
16037
  for (const group of parsed.tagGroups) {
15390
16038
  displayNames.set(group.name.toLowerCase(), group.name);
15391
16039
  }
16040
+ const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
15392
16041
  const colorOff = parsed.options?.color === "off";
15393
16042
  for (const c of layout.containers) {
15394
16043
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
@@ -15435,6 +16084,18 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15435
16084
  cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
15436
16085
  cG.append("rect").attr("x", COLLAPSE_BAR_INSET).attr("y", c.height - COLLAPSE_BAR_HEIGHT).attr("width", c.width - COLLAPSE_BAR_INSET * 2).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", containerStroke(palette, colorOff ? void 0 : c.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
15437
16086
  }
16087
+ if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
16088
+ const iconSize = 14;
16089
+ const iconPad = 5;
16090
+ const iconX = c.width - iconSize - iconPad;
16091
+ const iconY = iconPad;
16092
+ const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("transform", `translate(${iconX}, ${iconY})`);
16093
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
16094
+ const cx = iconSize / 2;
16095
+ const cy = iconSize / 2;
16096
+ 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);
16097
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
16098
+ }
15438
16099
  }
15439
16100
  for (const edge of layout.edges) {
15440
16101
  if (edge.points.length < 2) continue;
@@ -15493,6 +16154,58 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15493
16154
  nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
15494
16155
  nodeG.append("rect").attr("x", COLLAPSE_BAR_INSET).attr("y", node.height - COLLAPSE_BAR_HEIGHT).attr("width", node.width - COLLAPSE_BAR_INSET * 2).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", nodeStroke(palette, colorOff ? void 0 : node.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
15495
16156
  }
16157
+ if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
16158
+ const iconSize = 14;
16159
+ const iconPad = 5;
16160
+ const iconX = node.width - iconSize - iconPad;
16161
+ const iconY = iconPad;
16162
+ const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("transform", `translate(${iconX}, ${iconY})`);
16163
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
16164
+ const cx = iconSize / 2;
16165
+ const cy = iconSize / 2;
16166
+ 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);
16167
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
16168
+ }
16169
+ }
16170
+ if (hasAncestorTrail) {
16171
+ const rootNode = layout.nodes.find((n) => rootNodeIds.has(n.id));
16172
+ const rootContainer = !rootNode ? layout.containers.find((c) => rootNodeIds.has(c.nodeId)) : null;
16173
+ const rootCenterX = rootNode ? rootNode.x : rootContainer ? rootContainer.x + rootContainer.width / 2 : null;
16174
+ const rootTopY = rootNode ? rootNode.y : rootContainer ? rootContainer.y : null;
16175
+ if (rootCenterX !== null && rootTopY !== null) {
16176
+ const trailBottomY = rootTopY - ANCESTOR_TRAIL_BOTTOM_GAP;
16177
+ const trailG = contentG.append("g").attr("class", "org-ancestor-trail");
16178
+ const count = ancestorPath.length;
16179
+ const dotPositions = [];
16180
+ for (let i = 0; i < count; i++) {
16181
+ const fromBottom = count - 1 - i;
16182
+ dotPositions.push(trailBottomY - fromBottom * ANCESTOR_ROW_HEIGHT);
16183
+ }
16184
+ const lineTopY = dotPositions[0];
16185
+ 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);
16186
+ for (let i = 0; i < count; i++) {
16187
+ const ancestor = ancestorPath[i];
16188
+ const dotY = dotPositions[i];
16189
+ const resolvedColor = ancestor.color ?? resolveTagColor(
16190
+ ancestor.metadata,
16191
+ parsed.tagGroups,
16192
+ activeTagGroup ?? null,
16193
+ ancestor.isContainer
16194
+ );
16195
+ const dotColor = resolvedColor ?? palette.textMuted;
16196
+ const rowG = trailG.append("g").attr("class", "org-ancestor-node").attr("data-focus-ancestor", ancestor.id).style("cursor", "pointer").attr("transform", `translate(${rootCenterX}, ${dotY})`);
16197
+ 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");
16198
+ rowG.append("circle").attr("cx", 0).attr("cy", 0).attr("r", ANCESTOR_DOT_R).attr("fill", dotColor);
16199
+ 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);
16200
+ rowG.on("mouseenter", function() {
16201
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R + 1);
16202
+ d3Selection.select(this).select("text").attr("fill", palette.text);
16203
+ }).on("mouseleave", function() {
16204
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R);
16205
+ d3Selection.select(this).select("text").attr("fill", palette.textMuted);
16206
+ });
16207
+ }
16208
+ }
15496
16209
  }
15497
16210
  if (fixedLegend || legendOnly || exportDims && hasLegend) {
15498
16211
  const groups = layout.legend.map((g) => ({
@@ -15589,13 +16302,14 @@ function renderOrgForExport(content, theme, palette) {
15589
16302
  return extractExportSvg(container, theme);
15590
16303
  });
15591
16304
  }
15592
- var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, LEGEND_FIXED_GAP;
16305
+ var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
15593
16306
  var init_renderer = __esm({
15594
16307
  "src/org/renderer.ts"() {
15595
16308
  "use strict";
15596
16309
  init_fonts();
15597
16310
  init_export_container();
15598
16311
  init_color_utils();
16312
+ init_tag_groups();
15599
16313
  init_parser4();
15600
16314
  init_layout();
15601
16315
  init_legend_constants();
@@ -15619,6 +16333,10 @@ var init_renderer = __esm({
15619
16333
  CONTAINER_HEADER_HEIGHT = 28;
15620
16334
  COLLAPSE_BAR_HEIGHT = 6;
15621
16335
  COLLAPSE_BAR_INSET = 0;
16336
+ ANCESTOR_DOT_R = 4;
16337
+ ANCESTOR_LABEL_FONT_SIZE = 11;
16338
+ ANCESTOR_ROW_HEIGHT = 22;
16339
+ ANCESTOR_TRAIL_BOTTOM_GAP = 16;
15622
16340
  LEGEND_FIXED_GAP = 8;
15623
16341
  }
15624
16342
  });
@@ -19342,7 +20060,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
19342
20060
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
19343
20061
  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);
19344
20062
  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");
19345
- 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);
20063
+ const maxLabelLines = Math.max(
20064
+ 2,
20065
+ Math.floor((group.height - 16) / (MIN_NODE_FONT_SIZE * 1.3))
20066
+ );
20067
+ const fitted = fitLabelToHeader(group.label, group.width, maxLabelLines);
20068
+ const lineH = fitted.fontSize * 1.3;
20069
+ const totalH = fitted.lines.length * lineH;
20070
+ for (let li = 0; li < fitted.lines.length; li++) {
20071
+ 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]);
20072
+ }
19346
20073
  } else {
19347
20074
  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);
19348
20075
  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);
@@ -30603,7 +31330,22 @@ function renderQuarterCircle(svg, parsed, quadrant, qColor, palette, isDark, wid
30603
31330
  const innerR = ri * ringBandWidth;
30604
31331
  const outerR = (ri + 1) * ringBandWidth;
30605
31332
  const fillColor = ri % 2 === 0 ? palette.bg : mix(palette.bg, palette.border, 0.15);
31333
+ const ringName = parsed.rings[ri].name;
30606
31334
  svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", fillColor).attr("stroke", mutedColor).attr("stroke-width", 0.5);
31335
+ svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", "transparent").attr("data-ring-arc", ringName).style("cursor", "pointer").on("mouseenter", () => {
31336
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").each(function() {
31337
+ const el = d3Selection14.select(this);
31338
+ const isMatch = this.getAttribute("data-ring-arc") === ringName;
31339
+ el.attr("fill", isMatch ? qColor : "transparent").attr(
31340
+ "opacity",
31341
+ isMatch ? 0.15 : 1
31342
+ );
31343
+ });
31344
+ dimExceptRing(rootContainer, ringName);
31345
+ }).on("mouseleave", () => {
31346
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").attr("fill", "transparent").attr("opacity", 1);
31347
+ clearDim(rootContainer);
31348
+ });
30607
31349
  }
30608
31350
  const ringOrder = parsed.rings.map((r) => r.name);
30609
31351
  const angularPadding = 0.08;
@@ -30666,10 +31408,21 @@ function dimExcept(root, lineNum) {
30666
31408
  el.style.opacity = el.getAttribute("data-line-number") === lineNum ? "1" : String(DIM_OPACITY);
30667
31409
  });
30668
31410
  }
31411
+ function dimExceptRing(root, ringName) {
31412
+ root.querySelectorAll("[data-line-number]").forEach((el) => {
31413
+ el.style.opacity = el.getAttribute("data-ring") === ringName ? "1" : String(DIM_OPACITY);
31414
+ });
31415
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31416
+ el.style.opacity = el.getAttribute("data-ring-group") === ringName ? "1" : String(DIM_OPACITY);
31417
+ });
31418
+ }
30669
31419
  function clearDim(root) {
30670
31420
  root.querySelectorAll("[data-line-number]").forEach((el) => {
30671
31421
  el.style.opacity = "1";
30672
31422
  });
31423
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31424
+ el.style.opacity = "1";
31425
+ });
30673
31426
  }
30674
31427
  function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootContainer, onClickItem) {
30675
31428
  const ringOrder = parsed.rings.map((r) => r.name);
@@ -30681,11 +31434,13 @@ function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootC
30681
31434
  const blips = quadrant.blips.filter((b) => b.ring === ringName);
30682
31435
  if (blips.length === 0) continue;
30683
31436
  const ringGroup = document.createElement("div");
31437
+ ringGroup.setAttribute("data-ring-group", ringName);
30684
31438
  ringGroup.style.cssText = `
30685
31439
  background: ${palette.surface};
30686
31440
  border-radius: 8px;
30687
31441
  padding: 10px;
30688
31442
  margin-bottom: 12px;
31443
+ transition: opacity 0.15s;
30689
31444
  `;
30690
31445
  const header = document.createElement("div");
30691
31446
  header.style.cssText = `
@@ -31823,7 +32578,8 @@ function layoutJourneyMap(parsed, palette, options) {
31823
32578
  const annoIconIndent = ANNO_ICON_SIZE + ANNO_ICON_GAP;
31824
32579
  const annoTextW = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2 - annoIconIndent;
31825
32580
  const descTextWidth = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2;
31826
- const charWidth = 4.8;
32581
+ const FONT_SIZE_META2 = 10;
32582
+ const charWidth = FONT_SIZE_META2 * 0.6;
31827
32583
  const titleTextWidth = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2;
31828
32584
  const titleCharWidth = 6.5;
31829
32585
  const TITLE_LINE_HEIGHT2 = 16;
@@ -32039,7 +32795,13 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32039
32795
  const containerW = exportDims?.width ?? container.clientWidth;
32040
32796
  const containerH = exportDims?.height ?? container.clientHeight;
32041
32797
  const useContainerFit = !exportDims && containerW > 0 && containerH > 0;
32042
- const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", useContainerFit ? containerW : layout.totalWidth).attr("height", useContainerFit ? containerH : layout.totalHeight).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32798
+ const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr(
32799
+ "width",
32800
+ useContainerFit ? containerW : exportDims?.width ?? layout.totalWidth
32801
+ ).attr(
32802
+ "height",
32803
+ useContainerFit ? containerH : exportDims?.height ?? layout.totalHeight
32804
+ ).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32043
32805
  svg.append("rect").attr("width", layout.totalWidth).attr("height", layout.totalHeight).attr("fill", palette.bg);
32044
32806
  const defs = svg.append("defs");
32045
32807
  const curveGradient = defs.append("linearGradient").attr("id", "journey-curve-gradient").attr("x1", "0").attr("y1", "0").attr("x2", "0").attr("y2", "1");
@@ -32111,7 +32873,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32111
32873
  const legendY = PADDING2 + (parsed.title ? FONT_SIZE_TITLE + 8 : 0);
32112
32874
  const legendG = svg.append("g").attr("class", "journey-legend").attr("transform", `translate(${legendX},${legendY})`);
32113
32875
  const legendConfig = {
32114
- groups: allLegendGroups,
32876
+ groups: parsed.tagGroups,
32115
32877
  position: {
32116
32878
  placement: "top-center",
32117
32879
  titleRelation: "inline-with-title"
@@ -32180,7 +32942,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32180
32942
  const y = layout.curveAreaBottom - (score - 1) / 4 * (layout.curveAreaBottom - layout.curveAreaTop - 120) - 10;
32181
32943
  curveG.append("line").attr("x1", PADDING2).attr("x2", layout.totalWidth - PADDING2).attr("y1", y).attr("y2", y).attr("stroke", palette.textMuted).attr("stroke-opacity", GRID_LINE_OPACITY).attr("stroke-dasharray", "4,4");
32182
32944
  const SCORE_LABEL_R = 8;
32183
- const labelG = curveG.append("g").attr("class", "journey-score-label");
32945
+ const labelG = curveG.append("g").attr("class", "journey-score-label").attr("data-score", String(score));
32184
32946
  renderScoreFace(
32185
32947
  labelG,
32186
32948
  PADDING2 - SCORE_LABEL_R - 2,
@@ -32190,35 +32952,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32190
32952
  SCORE_LABEL_R
32191
32953
  );
32192
32954
  if (!exportDims) {
32193
- const scoreStr = String(score);
32194
32955
  labelG.style("cursor", "pointer");
32195
- labelG.on("mouseenter", () => {
32196
- svg.selectAll(".journey-step").each(function() {
32197
- const hit = this.getAttribute("data-score") === scoreStr;
32198
- d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
32199
- });
32200
- svg.selectAll(".journey-face").each(function() {
32201
- const hit = this.getAttribute("data-score") === scoreStr;
32202
- const sel = d3.select(this);
32203
- sel.style("opacity", hit ? "1" : String(DIM_HOVER));
32204
- if (hit) {
32205
- const fcx = parseFloat(sel.attr("data-cx") ?? "0");
32206
- const fcy = parseFloat(sel.attr("data-cy") ?? "0");
32207
- sel.attr(
32208
- "transform",
32209
- `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
32210
- );
32211
- } else {
32212
- sel.attr("transform", null);
32213
- }
32214
- });
32215
- svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
32216
- });
32217
- labelG.on("mouseleave", () => {
32218
- svg.selectAll(".journey-step").style("opacity", null);
32219
- svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
32220
- svg.selectAll(".journey-thought").style("opacity", null);
32221
- });
32222
32956
  }
32223
32957
  }
32224
32958
  if (layout.curvePoints.length >= 2) {
@@ -32404,6 +33138,41 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32404
33138
  if (!exportDims) {
32405
33139
  const DIM_OPACITY2 = 0.35;
32406
33140
  let lockedLine = null;
33141
+ let lockedScore = null;
33142
+ const applyScoreDimming = (activeScore) => {
33143
+ const scoreStr = String(activeScore);
33144
+ svg.selectAll(".journey-step").each(function() {
33145
+ const hit = this.getAttribute("data-score") === scoreStr;
33146
+ d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
33147
+ });
33148
+ svg.selectAll(".journey-face").each(function() {
33149
+ const hit = this.getAttribute("data-score") === scoreStr;
33150
+ const sel = d3.select(this);
33151
+ sel.style("opacity", hit ? "1" : String(DIM_HOVER));
33152
+ if (hit) {
33153
+ const fcx = parseFloat(sel.attr("data-cx") ?? "0");
33154
+ const fcy = parseFloat(sel.attr("data-cy") ?? "0");
33155
+ sel.attr(
33156
+ "transform",
33157
+ `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
33158
+ );
33159
+ } else {
33160
+ sel.attr("transform", null);
33161
+ }
33162
+ });
33163
+ svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
33164
+ svg.selectAll(".journey-score-label").each(function() {
33165
+ const sel = d3.select(this);
33166
+ const s = sel.attr("data-score");
33167
+ sel.style("opacity", s === scoreStr ? "1" : String(DIM_HOVER));
33168
+ });
33169
+ };
33170
+ const clearScoreDimming = () => {
33171
+ svg.selectAll(".journey-step").style("opacity", null);
33172
+ svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
33173
+ svg.selectAll(".journey-thought").style("opacity", null);
33174
+ svg.selectAll(".journey-score-label").style("opacity", null);
33175
+ };
32407
33176
  const applyDimming = (activeLine) => {
32408
33177
  svg.selectAll(".journey-step").each(function() {
32409
33178
  const el = d3.select(this);
@@ -32458,7 +33227,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32458
33227
  const lines = wrapText4(thoughtText, THOUGHT_MAX_W, THOUGHT_FONT);
32459
33228
  const textW = Math.min(
32460
33229
  THOUGHT_MAX_W,
32461
- Math.max(...lines.map((l) => l.length * 4.8))
33230
+ Math.max(...lines.map((l) => l.length * THOUGHT_FONT * 0.6))
32462
33231
  );
32463
33232
  const bw = textW + THOUGHT_PAD_X * 2;
32464
33233
  const bh = lines.length * THOUGHT_LINE_H + THOUGHT_PAD_Y * 2;
@@ -32479,9 +33248,11 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32479
33248
  };
32480
33249
  svg.on("click", (event) => {
32481
33250
  const target = event.target;
32482
- if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase")) {
33251
+ if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase") && !target.closest(".journey-score-label")) {
32483
33252
  lockedLine = null;
33253
+ lockedScore = null;
32484
33254
  clearDimming();
33255
+ clearScoreDimming();
32485
33256
  }
32486
33257
  });
32487
33258
  const showThoughtForLine = (ln) => {
@@ -32495,17 +33266,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32495
33266
  svg.selectAll(".journey-face").each(function() {
32496
33267
  const el = d3.select(this);
32497
33268
  el.on("mouseenter", () => {
32498
- if (lockedLine !== null) return;
33269
+ if (lockedLine !== null || lockedScore !== null) return;
32499
33270
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32500
33271
  if (ln) {
32501
33272
  applyDimming(ln);
32502
33273
  showThoughtForLine(ln);
32503
33274
  }
32504
33275
  }).on("mouseleave", () => {
32505
- if (lockedLine !== null) return;
33276
+ if (lockedLine !== null || lockedScore !== null) return;
32506
33277
  clearDimming();
32507
33278
  }).on("click", (event) => {
32508
33279
  event.stopPropagation();
33280
+ if (lockedScore !== null) {
33281
+ lockedScore = null;
33282
+ clearScoreDimming();
33283
+ return;
33284
+ }
32509
33285
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32510
33286
  if (lockedLine === ln) {
32511
33287
  lockedLine = null;
@@ -32521,17 +33297,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32521
33297
  svg.selectAll(".journey-step").each(function() {
32522
33298
  const el = d3.select(this);
32523
33299
  el.on("mouseenter", () => {
32524
- if (lockedLine !== null) return;
33300
+ if (lockedLine !== null || lockedScore !== null) return;
32525
33301
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32526
33302
  if (ln) {
32527
33303
  applyDimming(ln);
32528
33304
  showThoughtForLine(ln);
32529
33305
  }
32530
33306
  }).on("mouseleave", () => {
32531
- if (lockedLine !== null) return;
33307
+ if (lockedLine !== null || lockedScore !== null) return;
32532
33308
  clearDimming();
32533
33309
  }).on("click", (event) => {
32534
33310
  event.stopPropagation();
33311
+ if (lockedScore !== null) {
33312
+ lockedScore = null;
33313
+ clearScoreDimming();
33314
+ return;
33315
+ }
32535
33316
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32536
33317
  if (lockedLine === ln) {
32537
33318
  lockedLine = null;
@@ -32544,6 +33325,30 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32544
33325
  }
32545
33326
  });
32546
33327
  });
33328
+ svg.selectAll(".journey-score-label").each(function() {
33329
+ const el = d3.select(this);
33330
+ const score = parseInt(el.attr("data-score") ?? "0", 10);
33331
+ el.on("mouseenter", () => {
33332
+ if (lockedLine !== null || lockedScore !== null) return;
33333
+ applyScoreDimming(score);
33334
+ }).on("mouseleave", () => {
33335
+ if (lockedLine !== null || lockedScore !== null) return;
33336
+ clearScoreDimming();
33337
+ }).on("click", (event) => {
33338
+ event.stopPropagation();
33339
+ if (lockedLine !== null) {
33340
+ lockedLine = null;
33341
+ clearDimming();
33342
+ }
33343
+ if (lockedScore === score) {
33344
+ lockedScore = null;
33345
+ clearScoreDimming();
33346
+ } else {
33347
+ lockedScore = score;
33348
+ applyScoreDimming(score);
33349
+ }
33350
+ });
33351
+ });
32547
33352
  }
32548
33353
  }
32549
33354
  function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
@@ -32695,8 +33500,8 @@ function renderScoreFace(parent, cx, cy, score, palette, radius) {
32695
33500
  ).attr("fill", "none").attr("stroke", eyeColor).attr("stroke-width", 1.2).attr("stroke-linecap", "round");
32696
33501
  return g;
32697
33502
  }
32698
- function wrapText4(text, maxWidth, _fontSize) {
32699
- const charWidth = 4.8;
33503
+ function wrapText4(text, maxWidth, fontSize) {
33504
+ const charWidth = fontSize * 0.6;
32700
33505
  const maxChars = Math.floor(maxWidth / charWidth);
32701
33506
  if (maxChars <= 0) return [text];
32702
33507
  const words = text.split(/\s+/);
@@ -32715,7 +33520,7 @@ function wrapText4(text, maxWidth, _fontSize) {
32715
33520
  return lines;
32716
33521
  }
32717
33522
  function truncateText(text, maxWidth) {
32718
- const maxChars = Math.floor(maxWidth / 4.8);
33523
+ const maxChars = Math.floor(maxWidth / 6.6);
32719
33524
  if (text.length <= maxChars) return text;
32720
33525
  return text.substring(0, maxChars - 1) + "\u2026";
32721
33526
  }
@@ -32815,6 +33620,21 @@ var init_renderer13 = __esm({
32815
33620
  }
32816
33621
  });
32817
33622
 
33623
+ // src/cycle/types.ts
33624
+ function arrowHeadLength(strokeWidth) {
33625
+ return BASE_ARROW_SIZE + ARROW_SCALE * Math.sqrt(strokeWidth);
33626
+ }
33627
+ var DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH, BASE_ARROW_SIZE, ARROW_SCALE;
33628
+ var init_types2 = __esm({
33629
+ "src/cycle/types.ts"() {
33630
+ "use strict";
33631
+ DEFAULT_EDGE_WIDTH = 3;
33632
+ MIN_EDGE_WIDTH = 2;
33633
+ BASE_ARROW_SIZE = 8;
33634
+ ARROW_SCALE = 6;
33635
+ }
33636
+ });
33637
+
32818
33638
  // src/cycle/layout.ts
32819
33639
  function computeCycleLayout(parsed, options) {
32820
33640
  const width = options?.width ?? 800;
@@ -33149,8 +33969,11 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33149
33969
  return parsed.edges.map((edge) => {
33150
33970
  const src = layoutNodes[edge.sourceIndex];
33151
33971
  const tgt = layoutNodes[edge.targetIndex];
33152
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH;
33153
- const arrowLen = ARROWHEAD_MARKER_W * strokeWidth;
33972
+ const strokeWidth = Math.max(
33973
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33974
+ MIN_EDGE_WIDTH
33975
+ );
33976
+ const arrowLen = arrowHeadLength(strokeWidth) * 0.9;
33154
33977
  const { path, labelX, labelY, labelAngle } = buildEdgeArc(
33155
33978
  src,
33156
33979
  tgt,
@@ -33172,7 +33995,7 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33172
33995
  });
33173
33996
  }
33174
33997
  function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClockwise) {
33175
- const PADDING3 = 10;
33998
+ const PADDING3 = 30;
33176
33999
  let contentMinX = Infinity, contentMaxX = -Infinity;
33177
34000
  let contentMinY = Infinity, contentMaxY = -Infinity;
33178
34001
  for (const n of nodes) {
@@ -33225,17 +34048,7 @@ function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClo
33225
34048
  }
33226
34049
  function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33227
34050
  const dir = isClockwise ? 1 : -1;
33228
- const startAngle = src.isCircle ? circleNodeExitAngle(src.width / 2, radius, src.angle, dir) : circleRectExitAngle(
33229
- src.x,
33230
- src.y,
33231
- src.width / 2,
33232
- src.height / 2,
33233
- cx,
33234
- cy,
33235
- radius,
33236
- src.angle,
33237
- dir
33238
- );
34051
+ const startAngle = src.angle;
33239
34052
  const nodeEndAngle = tgt.isCircle ? circleNodeExitAngle(tgt.width / 2, radius, tgt.angle, -dir) : circleRectExitAngle(
33240
34053
  tgt.x,
33241
34054
  tgt.y,
@@ -33265,10 +34078,11 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33265
34078
  const labelY = cy + labelR * Math.sin(midAngle);
33266
34079
  return { path, labelX, labelY, labelAngle: midAngle };
33267
34080
  }
33268
- var MIN_ARC_ANGLE, LABEL_CHAR_W, CIRCLE_LABEL_CHAR_W, DESC_CHAR_W, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, DEFAULT_EDGE_WIDTH, ARROWHEAD_MARKER_W, EDGE_LABEL_CHAR_W;
34081
+ var MIN_ARC_ANGLE, LABEL_CHAR_W, CIRCLE_LABEL_CHAR_W, DESC_CHAR_W, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, EDGE_LABEL_CHAR_W;
33269
34082
  var init_layout13 = __esm({
33270
34083
  "src/cycle/layout.ts"() {
33271
34084
  "use strict";
34085
+ init_types2();
33272
34086
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
33273
34087
  LABEL_CHAR_W = 8;
33274
34088
  CIRCLE_LABEL_CHAR_W = 10;
@@ -33282,8 +34096,6 @@ var init_layout13 = __esm({
33282
34096
  NODE_PAD_X = 20;
33283
34097
  MIN_CIRCLE_RADIUS = 35;
33284
34098
  CIRCLE_PAD = 14;
33285
- DEFAULT_EDGE_WIDTH = 3;
33286
- ARROWHEAD_MARKER_W = 8;
33287
34099
  EDGE_LABEL_CHAR_W = 7;
33288
34100
  }
33289
34101
  });
@@ -33367,19 +34179,25 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
33367
34179
  const g = svg.append("g").attr("transform", `translate(0, ${diagramTop})`);
33368
34180
  const defs = svg.append("defs");
33369
34181
  const defaultNodeColor = palette.primary;
33370
- const arrowColors = /* @__PURE__ */ new Set();
33371
- for (let i = 0; i < parsed.edges.length; i++) {
33372
- const edge = parsed.edges[i];
34182
+ const markerKeys = /* @__PURE__ */ new Set();
34183
+ for (const edge of parsed.edges) {
33373
34184
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33374
- arrowColors.add(color);
34185
+ const sw = Math.max(edge.width ?? DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH);
34186
+ const key = `${color}|${sw}`;
34187
+ if (!markerKeys.has(key)) {
34188
+ markerKeys.add(key);
34189
+ ensureArrowMarker(defs, color, sw);
34190
+ }
33375
34191
  }
33376
- ensureArrowMarkers2(defs, arrowColors);
33377
34192
  for (let i = 0; i < layout.edges.length; i++) {
33378
34193
  const le = layout.edges[i];
33379
34194
  const edge = parsed.edges[i];
33380
34195
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33381
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH2;
33382
- const markerId = `cycle-arrow-${color.replace("#", "")}`;
34196
+ const strokeWidth = Math.max(
34197
+ edge.width ?? DEFAULT_EDGE_WIDTH,
34198
+ MIN_EDGE_WIDTH
34199
+ );
34200
+ const markerId = arrowMarkerId(color, strokeWidth);
33383
34201
  const edgeG = g.append("g").attr("class", "cycle-edge");
33384
34202
  if (edge.lineNumber) {
33385
34203
  edgeG.attr("data-line-number", edge.lineNumber);
@@ -33525,16 +34343,16 @@ function resolveEdgeColor(edge, parsed, palette, defaultNodeColor) {
33525
34343
  }
33526
34344
  return defaultNodeColor;
33527
34345
  }
33528
- function ensureArrowMarkers2(defs, colors) {
33529
- for (const color of colors) {
33530
- const id = `cycle-arrow-${color.replace("#", "")}`;
33531
- defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${ARROWHEAD_W5 * 2} ${ARROWHEAD_H5 * 2}`).attr("refX", 0).attr("refY", ARROWHEAD_H5).attr("markerWidth", ARROWHEAD_W5).attr("markerHeight", ARROWHEAD_H5).attr("orient", "auto").append("polygon").attr(
33532
- "points",
33533
- `0,0 ${ARROWHEAD_W5 * 2},${ARROWHEAD_H5} 0,${ARROWHEAD_H5 * 2}`
33534
- ).attr("fill", color);
33535
- }
34346
+ function arrowMarkerId(color, strokeWidth) {
34347
+ return `cycle-arrow-${color.replace("#", "")}-w${strokeWidth}`;
34348
+ }
34349
+ function ensureArrowMarker(defs, color, strokeWidth) {
34350
+ const id = arrowMarkerId(color, strokeWidth);
34351
+ const mw = arrowHeadLength(strokeWidth) / strokeWidth;
34352
+ const mh = Math.max(1.5, mw * 0.5);
34353
+ 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);
33536
34354
  }
33537
- var DEFAULT_EDGE_WIDTH2, ARROWHEAD_W5, ARROWHEAD_H5, NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
34355
+ var NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33538
34356
  var init_renderer14 = __esm({
33539
34357
  "src/cycle/renderer.ts"() {
33540
34358
  "use strict";
@@ -33545,10 +34363,8 @@ var init_renderer14 = __esm({
33545
34363
  init_color_utils();
33546
34364
  init_colors();
33547
34365
  init_inline_markdown();
34366
+ init_types2();
33548
34367
  init_layout13();
33549
- DEFAULT_EDGE_WIDTH2 = 3;
33550
- ARROWHEAD_W5 = 8;
33551
- ARROWHEAD_H5 = 8;
33552
34368
  NODE_FONT_SIZE5 = 13;
33553
34369
  DESC_FONT_SIZE5 = 11;
33554
34370
  EDGE_LABEL_FONT_SIZE9 = 11;
@@ -33557,6 +34373,397 @@ var init_renderer14 = __esm({
33557
34373
  }
33558
34374
  });
33559
34375
 
34376
+ // src/pyramid/renderer.ts
34377
+ var renderer_exports15 = {};
34378
+ __export(renderer_exports15, {
34379
+ renderPyramid: () => renderPyramid,
34380
+ renderPyramidForExport: () => renderPyramidForExport
34381
+ });
34382
+ import * as d3Selection17 from "d3-selection";
34383
+ function renderPyramid(container, parsed, palette, isDark, onClickItem, exportDims) {
34384
+ if (parsed.layers.length === 0) return;
34385
+ d3Selection17.select(container).selectAll(":not([data-d3-tooltip])").remove();
34386
+ const width = exportDims?.width ?? container.clientWidth;
34387
+ const height = exportDims?.height ?? container.clientHeight;
34388
+ if (width <= 0 || height <= 0) return;
34389
+ const hasAnyDescription = parsed.layers.some((l) => l.description.length > 0);
34390
+ const titleH = parsed.title ? TITLE_AREA_HEIGHT2 : 0;
34391
+ const bodyTop = titleH + V_MARGIN;
34392
+ const bodyBottom = height - V_MARGIN;
34393
+ const bodyHeight = Math.max(60, bodyBottom - bodyTop);
34394
+ const sideMargin = width * H_MARGIN_FRAC;
34395
+ const usableWidth = width - sideMargin * 2;
34396
+ const N = parsed.layers.length;
34397
+ const singleProbe = computeLayout2({
34398
+ width,
34399
+ usableWidth,
34400
+ sideMargin,
34401
+ bodyTop,
34402
+ bodyHeight,
34403
+ layers: parsed.layers,
34404
+ hasDescription: hasAnyDescription,
34405
+ alternate: false
34406
+ });
34407
+ const anyOverflow = singleProbe.wraps.some((w) => w.overflows);
34408
+ const useAlternate = anyOverflow && hasAnyDescription && N >= 2;
34409
+ const layout = useAlternate ? computeLayout2({
34410
+ width,
34411
+ usableWidth,
34412
+ sideMargin,
34413
+ bodyTop,
34414
+ bodyHeight,
34415
+ layers: parsed.layers,
34416
+ hasDescription: true,
34417
+ alternate: true
34418
+ }) : singleProbe;
34419
+ 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);
34420
+ svg.append("style").text(
34421
+ ".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}"
34422
+ );
34423
+ svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
34424
+ if (parsed.title) {
34425
+ 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");
34426
+ if (onClickItem) {
34427
+ titleText.on("click", () => onClickItem(parsed.titleLineNumber));
34428
+ }
34429
+ }
34430
+ const seriesColors2 = getSeriesColors(palette);
34431
+ const layerBase = isDark ? palette.surface : palette.bg;
34432
+ const resolveSolid = (layer, i) => {
34433
+ if (layer.color) {
34434
+ const named = resolveColor(layer.color, palette);
34435
+ if (named) return named;
34436
+ }
34437
+ return seriesColors2[i % seriesColors2.length];
34438
+ };
34439
+ const diagramG = svg.append("g").attr("class", "pyramid-body");
34440
+ for (let i = 0; i < N; i++) {
34441
+ const layer = parsed.layers[i];
34442
+ const topEdgeY = layout.pyramidTop + i * layout.layerH;
34443
+ const botEdgeY = topEdgeY + layout.layerH;
34444
+ const topHalf = halfWidthAt(i, N, layout.baseWidth, parsed.inverted);
34445
+ const botHalf = halfWidthAt(i + 1, N, layout.baseWidth, parsed.inverted);
34446
+ const polyPoints = [
34447
+ [layout.pyramidCx - topHalf, topEdgeY],
34448
+ [layout.pyramidCx + topHalf, topEdgeY],
34449
+ [layout.pyramidCx + botHalf, botEdgeY],
34450
+ [layout.pyramidCx - botHalf, botEdgeY]
34451
+ ].map((p) => `${p[0]},${p[1]}`).join(" ");
34452
+ const solidColor = resolveSolid(layer, i);
34453
+ const fillColor = mix(solidColor, layerBase, 30);
34454
+ const layerG = diagramG.append("g").attr("class", "pyramid-layer").attr("data-line-number", layer.lineNumber);
34455
+ if (onClickItem) {
34456
+ const ln = layer.lineNumber;
34457
+ layerG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34458
+ }
34459
+ layerG.append("polygon").attr("points", polyPoints).attr("fill", fillColor).attr("stroke", solidColor).attr("stroke-width", 2);
34460
+ const midY = (topEdgeY + botEdgeY) / 2;
34461
+ const labelFitsInside = Math.min(topHalf, botHalf) * 2 > layout.labelFont * 4;
34462
+ const textColor = labelFitsInside ? contrastText(fillColor, "#eceff4", "#2e3440") : palette.text;
34463
+ 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);
34464
+ renderInlineText(labelText, layer.label, palette);
34465
+ if (layer.description.length > 0) {
34466
+ const side = useAlternate ? i % 2 === 0 ? "right" : "left" : "right";
34467
+ const wrap = layout.wraps[i];
34468
+ renderLayerDescriptions(
34469
+ diagramG,
34470
+ layer,
34471
+ side,
34472
+ wrap,
34473
+ layout,
34474
+ midY,
34475
+ solidColor,
34476
+ palette,
34477
+ titleH + V_MARGIN,
34478
+ height - V_MARGIN,
34479
+ onClickItem
34480
+ );
34481
+ }
34482
+ }
34483
+ }
34484
+ function renderPyramidForExport(container, parsed, palette, isDark, exportDims) {
34485
+ renderPyramid(container, parsed, palette, isDark, void 0, exportDims);
34486
+ }
34487
+ function computeLayout2(input) {
34488
+ const {
34489
+ width,
34490
+ usableWidth,
34491
+ sideMargin,
34492
+ bodyTop,
34493
+ bodyHeight,
34494
+ layers,
34495
+ hasDescription,
34496
+ alternate
34497
+ } = input;
34498
+ const N = layers.length;
34499
+ const pyramidShareFrac = hasDescription ? alternate ? PYRAMID_SHARE_ALTERNATE : PYRAMID_SHARE_WITH_DESC : BASE_WIDTH_FRAC_NO_DESC;
34500
+ const pyramidBandWidth = usableWidth * pyramidShareFrac;
34501
+ const maxBaseByHeight = bodyHeight / PITCH_RATIO;
34502
+ const baseWidth = Math.min(pyramidBandWidth, maxBaseByHeight);
34503
+ const pyramidH = baseWidth * PITCH_RATIO;
34504
+ let pyramidCx;
34505
+ if (!hasDescription) {
34506
+ pyramidCx = width / 2;
34507
+ } else if (alternate) {
34508
+ pyramidCx = width / 2;
34509
+ } else {
34510
+ pyramidCx = sideMargin + pyramidBandWidth / 2;
34511
+ }
34512
+ const pyramidTop = bodyTop + (bodyHeight - pyramidH) / 2;
34513
+ const layerH = pyramidH / N;
34514
+ const labelFont = clamp(
34515
+ Math.round(layerH * 0.38),
34516
+ LABEL_FONT_MIN,
34517
+ LABEL_FONT_MAX
34518
+ );
34519
+ const descFont = clamp(
34520
+ Math.round(layerH * 0.22),
34521
+ DESC_FONT_MIN,
34522
+ DESC_FONT_MAX
34523
+ );
34524
+ const descLineHeight = Math.round(descFont * 1.35);
34525
+ const pyramidRightEdge = pyramidCx + baseWidth / 2;
34526
+ const pyramidLeftEdge = pyramidCx - baseWidth / 2;
34527
+ const rightAccentX = pyramidRightEdge + DESC_GAP;
34528
+ const rightTextX = rightAccentX + DESC_ACCENT_WIDTH + DESC_ACCENT_GAP;
34529
+ const rightTextWidth = Math.max(80, width - sideMargin - rightTextX);
34530
+ const leftAccentX = pyramidLeftEdge - DESC_GAP - DESC_ACCENT_WIDTH;
34531
+ const leftTextWidth = Math.max(
34532
+ 80,
34533
+ leftAccentX - DESC_ACCENT_GAP - sideMargin
34534
+ );
34535
+ const leftTextX = sideMargin;
34536
+ const wraps = layers.map((layer, i) => {
34537
+ if (layer.description.length === 0) {
34538
+ return { allLines: [], overflows: false, shortLineCount: 0 };
34539
+ }
34540
+ const side = alternate ? i % 2 === 0 ? "right" : "left" : "right";
34541
+ const colWidth = side === "right" ? rightTextWidth : leftTextWidth;
34542
+ const wrapped = [];
34543
+ for (const line11 of layer.description) {
34544
+ wrapped.push(...wrapText5(line11, colWidth, descFont));
34545
+ }
34546
+ const bandCap = Math.max(1, Math.floor(layerH / descLineHeight) - 0);
34547
+ const overflows = wrapped.length > bandCap;
34548
+ const shortLineCount = overflows ? bandCap : wrapped.length;
34549
+ return { allLines: wrapped, overflows, shortLineCount };
34550
+ });
34551
+ return {
34552
+ alternate,
34553
+ baseWidth,
34554
+ pyramidCx,
34555
+ pyramidTop,
34556
+ pyramidH,
34557
+ layerH,
34558
+ labelFont,
34559
+ descFont,
34560
+ descLineHeight,
34561
+ rightTextX,
34562
+ rightTextWidth,
34563
+ leftTextX,
34564
+ leftTextWidth,
34565
+ rightAccentX,
34566
+ leftAccentX,
34567
+ wraps
34568
+ };
34569
+ }
34570
+ function halfWidthAt(edgeIdx, n, baseWidth, inverted) {
34571
+ const topNarrow = !inverted;
34572
+ const frac = topNarrow ? edgeIdx / n : (n - edgeIdx) / n;
34573
+ return frac * baseWidth / 2;
34574
+ }
34575
+ function renderLayerDescriptions(parentG, layer, side, wrap, layout, midY, accentColor, palette, topBound, bottomBound, onClickItem) {
34576
+ const { descFont, descLineHeight } = layout;
34577
+ const accentX = side === "right" ? layout.rightAccentX : layout.leftAccentX;
34578
+ const textX = side === "right" ? layout.rightTextX : layout.leftTextX;
34579
+ const textAnchor = side === "right" ? "start" : "end";
34580
+ const textLineX = side === "right" ? textX : layout.leftAccentX - DESC_ACCENT_GAP;
34581
+ const availableH = bottomBound - topBound;
34582
+ const fullMaxLines = Math.max(1, Math.floor(availableH / descLineHeight));
34583
+ const fullLines = truncateWithEllipsis(wrap.allLines, fullMaxLines);
34584
+ if (!wrap.overflows) {
34585
+ renderDescriptionVariant({
34586
+ parentG,
34587
+ layer,
34588
+ lines: wrap.allLines,
34589
+ className: "pyramid-desc",
34590
+ accentX,
34591
+ accentColor,
34592
+ textX: textLineX,
34593
+ textAnchor,
34594
+ midY,
34595
+ descFont,
34596
+ descLineHeight,
34597
+ palette,
34598
+ topBound,
34599
+ bottomBound,
34600
+ onClickItem,
34601
+ variant: "short"
34602
+ });
34603
+ return;
34604
+ }
34605
+ const shortLines = buildShortLines(wrap);
34606
+ renderDescriptionVariant({
34607
+ parentG,
34608
+ layer,
34609
+ lines: shortLines,
34610
+ className: "pyramid-desc pyramid-desc-short",
34611
+ accentX,
34612
+ accentColor,
34613
+ textX: textLineX,
34614
+ textAnchor,
34615
+ midY,
34616
+ descFont,
34617
+ descLineHeight,
34618
+ palette,
34619
+ topBound,
34620
+ bottomBound,
34621
+ onClickItem,
34622
+ variant: "short"
34623
+ });
34624
+ renderDescriptionVariant({
34625
+ parentG,
34626
+ layer,
34627
+ lines: fullLines,
34628
+ className: "pyramid-desc pyramid-desc-full",
34629
+ accentX,
34630
+ accentColor,
34631
+ textX: textLineX,
34632
+ textAnchor,
34633
+ midY,
34634
+ descFont,
34635
+ descLineHeight,
34636
+ palette,
34637
+ topBound,
34638
+ bottomBound,
34639
+ onClickItem,
34640
+ variant: "full"
34641
+ });
34642
+ }
34643
+ function truncateWithEllipsis(lines, maxLines) {
34644
+ if (lines.length <= maxLines) return lines.slice();
34645
+ const visible = lines.slice(0, maxLines);
34646
+ if (visible.length === 0) return visible;
34647
+ const last = visible[visible.length - 1];
34648
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34649
+ return visible;
34650
+ }
34651
+ function buildShortLines(wrap) {
34652
+ if (!wrap.overflows) return wrap.allLines.slice();
34653
+ const visible = wrap.allLines.slice(0, wrap.shortLineCount);
34654
+ if (visible.length === 0) return [];
34655
+ const last = visible[visible.length - 1];
34656
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34657
+ return visible;
34658
+ }
34659
+ function renderDescriptionVariant(args) {
34660
+ const {
34661
+ parentG,
34662
+ layer,
34663
+ lines,
34664
+ className,
34665
+ accentX,
34666
+ accentColor,
34667
+ textX,
34668
+ textAnchor,
34669
+ midY,
34670
+ descFont,
34671
+ descLineHeight,
34672
+ palette,
34673
+ topBound,
34674
+ bottomBound,
34675
+ onClickItem,
34676
+ variant
34677
+ } = args;
34678
+ if (lines.length === 0) return;
34679
+ const totalH = lines.length * descLineHeight;
34680
+ let startY = midY - totalH / 2 + descLineHeight / 2;
34681
+ const accentPad = Math.max(4, Math.round(descFont * 0.3));
34682
+ const blockTop = startY - descLineHeight / 2 - accentPad;
34683
+ const blockBottom = startY + (lines.length - 1) * descLineHeight + descLineHeight / 2 + accentPad;
34684
+ let shift = 0;
34685
+ if (blockTop < topBound) shift = topBound - blockTop;
34686
+ else if (blockBottom > bottomBound) shift = bottomBound - blockBottom;
34687
+ startY += shift;
34688
+ const accentTop = startY - descLineHeight / 2 - accentPad;
34689
+ const accentH = totalH + accentPad * 2;
34690
+ const descG = parentG.append("g").attr("class", className).attr("data-line-number", layer.lineNumber).attr("data-variant", variant);
34691
+ if (onClickItem) {
34692
+ const ln = layer.lineNumber;
34693
+ descG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34694
+ }
34695
+ 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);
34696
+ for (let j = 0; j < lines.length; j++) {
34697
+ 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);
34698
+ renderInlineText(t, lines[j], palette);
34699
+ }
34700
+ }
34701
+ function wrapText5(line11, maxWidth, fontSize) {
34702
+ if (line11 === "") return [""];
34703
+ const avgCharW = fontSize * CHAR_WIDTH_RATIO4;
34704
+ const maxChars = Math.max(8, Math.floor(maxWidth / avgCharW));
34705
+ const bulletMatch = line11.match(/^(\s*(?:•|-)\s+)(.*)$/);
34706
+ const indent = bulletMatch ? bulletMatch[1] : "";
34707
+ const body = bulletMatch ? bulletMatch[2] : line11;
34708
+ const hangingIndent = " ".repeat(indent.length);
34709
+ const words = body.split(/\s+/);
34710
+ const out = [];
34711
+ let current = indent;
34712
+ for (const word of words) {
34713
+ if (word === "") continue;
34714
+ const tentative = current.length === indent.length ? current + word : `${current} ${word}`;
34715
+ if (tentative.length <= maxChars) {
34716
+ current = tentative;
34717
+ } else {
34718
+ if (current.length > indent.length) out.push(current);
34719
+ if (word.length > maxChars - hangingIndent.length) {
34720
+ let remaining = word;
34721
+ while (remaining.length > maxChars - hangingIndent.length) {
34722
+ const slice = remaining.slice(0, maxChars - hangingIndent.length);
34723
+ out.push(hangingIndent + slice);
34724
+ remaining = remaining.slice(maxChars - hangingIndent.length);
34725
+ }
34726
+ current = remaining.length > 0 ? hangingIndent + remaining : "";
34727
+ } else {
34728
+ current = hangingIndent + word;
34729
+ }
34730
+ }
34731
+ }
34732
+ if (current.length > (out.length === 0 ? 0 : indent.length)) {
34733
+ if (current.trim().length > 0) out.push(current);
34734
+ }
34735
+ return out.length > 0 ? out : [line11];
34736
+ }
34737
+ function clamp(x, lo, hi) {
34738
+ return Math.max(lo, Math.min(hi, x));
34739
+ }
34740
+ var TITLE_AREA_HEIGHT2, H_MARGIN_FRAC, V_MARGIN, BASE_WIDTH_FRAC_NO_DESC, PYRAMID_SHARE_WITH_DESC, PYRAMID_SHARE_ALTERNATE, PITCH_RATIO, DESC_GAP, DESC_ACCENT_WIDTH, DESC_ACCENT_GAP, CHAR_WIDTH_RATIO4, LABEL_FONT_MIN, LABEL_FONT_MAX, DESC_FONT_MIN, DESC_FONT_MAX;
34741
+ var init_renderer15 = __esm({
34742
+ "src/pyramid/renderer.ts"() {
34743
+ "use strict";
34744
+ init_fonts();
34745
+ init_title_constants();
34746
+ init_color_utils();
34747
+ init_colors();
34748
+ init_inline_markdown();
34749
+ TITLE_AREA_HEIGHT2 = 50;
34750
+ H_MARGIN_FRAC = 0.03;
34751
+ V_MARGIN = 16;
34752
+ BASE_WIDTH_FRAC_NO_DESC = 0.78;
34753
+ PYRAMID_SHARE_WITH_DESC = 0.58;
34754
+ PYRAMID_SHARE_ALTERNATE = 0.42;
34755
+ PITCH_RATIO = 0.85;
34756
+ DESC_GAP = 28;
34757
+ DESC_ACCENT_WIDTH = 3;
34758
+ DESC_ACCENT_GAP = 12;
34759
+ CHAR_WIDTH_RATIO4 = 0.55;
34760
+ LABEL_FONT_MIN = 12;
34761
+ LABEL_FONT_MAX = 22;
34762
+ DESC_FONT_MIN = 11;
34763
+ DESC_FONT_MAX = 15;
34764
+ }
34765
+ });
34766
+
33560
34767
  // src/sequence/collapse.ts
33561
34768
  function applyCollapseProjection(parsed, collapsedGroups) {
33562
34769
  if (collapsedGroups.size === 0) {
@@ -33749,20 +34956,19 @@ var init_tag_resolution = __esm({
33749
34956
  });
33750
34957
 
33751
34958
  // src/sequence/renderer.ts
33752
- var renderer_exports15 = {};
33753
- __export(renderer_exports15, {
34959
+ var renderer_exports16 = {};
34960
+ __export(renderer_exports16, {
33754
34961
  applyGroupOrdering: () => applyGroupOrdering,
33755
34962
  applyPositionOverrides: () => applyPositionOverrides,
33756
34963
  buildNoteMessageMap: () => buildNoteMessageMap,
33757
34964
  buildRenderSequence: () => buildRenderSequence,
33758
- collectNoteLineNumbers: () => collectNoteLineNumbers,
33759
34965
  computeActivations: () => computeActivations,
33760
34966
  groupMessagesBySection: () => groupMessagesBySection,
33761
34967
  parseInlineMarkdown: () => parseInlineMarkdown,
33762
34968
  renderSequenceDiagram: () => renderSequenceDiagram,
33763
34969
  truncateBareUrl: () => truncateBareUrl
33764
34970
  });
33765
- import * as d3Selection17 from "d3-selection";
34971
+ import * as d3Selection18 from "d3-selection";
33766
34972
  function wrapTextLines(text, maxChars) {
33767
34973
  const rawLines = text.split("\n");
33768
34974
  const wrapped = [];
@@ -34118,7 +35324,7 @@ function applyGroupOrdering(participants, groups, messages = []) {
34118
35324
  return result;
34119
35325
  }
34120
35326
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
34121
- d3Selection17.select(container).selectAll("*").remove();
35327
+ d3Selection18.select(container).selectAll("*").remove();
34122
35328
  const { title, options: parsedOptions } = parsed;
34123
35329
  const effectiveCollapsedGroups = /* @__PURE__ */ new Set();
34124
35330
  for (const group of parsed.groups) {
@@ -34139,9 +35345,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34139
35345
  const groups = collapsed ? collapsed.groups : parsed.groups;
34140
35346
  const collapsedGroupIds = collapsed?.collapsedGroupIds ?? /* @__PURE__ */ new Map();
34141
35347
  const collapsedSections = options?.collapsedSections;
34142
- const expandedNoteLines = options?.expandedNoteLines;
34143
- const collapseNotesDisabled = parsedOptions["collapse-notes"]?.toLowerCase() === "no";
34144
- const isNoteExpanded = (note) => expandedNoteLines === void 0 || collapseNotesDisabled || expandedNoteLines.has(note.lineNumber);
34145
35348
  const sourceParticipants = collapsed ? collapsed.participants : parsed.participants;
34146
35349
  const participants = applyPositionOverrides(
34147
35350
  applyGroupOrdering(sourceParticipants, groups, messages)
@@ -34310,7 +35513,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34310
35513
  const note = els[j];
34311
35514
  const sc = isNoteAfterSelfCall(note);
34312
35515
  const maxW = noteEffectiveMaxW(note.participantId, note.position, sc);
34313
- const noteH = isNoteExpanded(note) ? computeNoteHeight(note.text, charsForWidth(maxW)) : COLLAPSED_NOTE_H;
35516
+ const noteH = computeNoteHeight(note.text, charsForWidth(maxW));
34314
35517
  totalExtent += noteH + NOTE_OFFSET_BELOW;
34315
35518
  j++;
34316
35519
  }
@@ -34506,7 +35709,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34506
35709
  prevNote.position,
34507
35710
  isNoteAfterSelfCall(prevNote)
34508
35711
  );
34509
- const prevNoteH = isNoteExpanded(prevNote) ? computeNoteHeight(prevNote.text, charsForWidth(prevMaxW)) : COLLAPSED_NOTE_H;
35712
+ const prevNoteH = computeNoteHeight(
35713
+ prevNote.text,
35714
+ charsForWidth(prevMaxW)
35715
+ );
34510
35716
  noteTopY = prevNoteY + prevNoteH + NOTE_OFFSET_BELOW;
34511
35717
  } else {
34512
35718
  noteTopY = stepY(si) + noteOffsetBelow(el);
@@ -34537,7 +35743,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34537
35743
  note.position,
34538
35744
  isNoteAfterSelfCall(note)
34539
35745
  );
34540
- const noteH = isNoteExpanded(note) ? computeNoteHeight(note.text, charsForWidth(maxW)) : COLLAPSED_NOTE_H;
35746
+ const noteH = computeNoteHeight(note.text, charsForWidth(maxW));
34541
35747
  contentBottomY = Math.max(
34542
35748
  contentBottomY,
34543
35749
  noteTopY + noteH + NOTE_TRAILING_GAP
@@ -34559,7 +35765,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34559
35765
  participants.forEach((p, i) => {
34560
35766
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
34561
35767
  });
34562
- const svg = d3Selection17.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
35768
+ const svg = d3Selection18.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
34563
35769
  const defs = svg.append("defs");
34564
35770
  defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
34565
35771
  "points",
@@ -34607,24 +35813,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34607
35813
  titleEl.attr("data-line-number", parsed.titleLineNumber);
34608
35814
  }
34609
35815
  }
34610
- const allNoteLineNumbers = [];
34611
- const collectNoteLines = (els) => {
34612
- for (const el of els) {
34613
- if (isSequenceNote(el)) {
34614
- allNoteLineNumbers.push(el.lineNumber);
34615
- } else if (isSequenceBlock(el)) {
34616
- collectNoteLines(el.children);
34617
- if ("elseChildren" in el) collectNoteLines(el.elseChildren);
34618
- if ("branches" in el && Array.isArray(el.branches)) {
34619
- for (const branch of el.branches) {
34620
- collectNoteLines(branch.children);
34621
- }
34622
- }
34623
- }
34624
- }
34625
- };
34626
- collectNoteLines(elements);
34627
- const showNotesControl = allNoteLineNumbers.length > 0 && !collapseNotesDisabled && expandedNoteLines !== void 0;
34628
35816
  const hasTagGroups = parsed.tagGroups.length > 0;
34629
35817
  const collapsedGroupNames = /* @__PURE__ */ new Set();
34630
35818
  const collapsedGroupMeta = /* @__PURE__ */ new Map();
@@ -34804,7 +35992,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34804
35992
  firstBranchStep = Math.min(firstBranchStep, first);
34805
35993
  }
34806
35994
  if (firstBranchStep < Infinity) {
34807
- const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
35995
+ const dividerY = stepY(firstBranchStep) - BLOCK_HEADER_SPACE;
34808
35996
  deferredLines.push({
34809
35997
  x1: frameX,
34810
35998
  y1: dividerY,
@@ -34831,7 +36019,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34831
36019
  firstElseStep = Math.min(firstElseStep, first);
34832
36020
  }
34833
36021
  if (firstElseStep < Infinity) {
34834
- const dividerY = stepY(firstElseStep) - stepSpacing / 2;
36022
+ const dividerY = stepY(firstElseStep) - BLOCK_HEADER_SPACE;
34835
36023
  deferredLines.push({
34836
36024
  x1: frameX,
34837
36025
  y1: dividerY,
@@ -35036,7 +36224,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35036
36224
  }
35037
36225
  });
35038
36226
  const noteFill = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.bg, palette.surface, 15);
35039
- const collapsedNoteFill = mix(palette.textMuted, palette.bg, 15);
35040
36227
  const renderNoteElements = (els) => {
35041
36228
  for (const el of els) {
35042
36229
  if (isSequenceNote(el)) {
@@ -35044,83 +36231,54 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35044
36231
  if (px === void 0) continue;
35045
36232
  const noteTopY = noteYMap.get(el);
35046
36233
  if (noteTopY === void 0) continue;
35047
- const expanded = isNoteExpanded(el);
35048
36234
  const isRight = el.position === "right";
35049
- if (expanded) {
35050
- const afterSelfCall = isNoteAfterSelfCall(el);
35051
- const maxW = noteEffectiveMaxW(
35052
- el.participantId,
35053
- el.position,
35054
- afterSelfCall
35055
- );
35056
- const maxChars = charsForWidth(maxW);
35057
- const wrappedLines = wrapTextLines(el.text, maxChars);
35058
- const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
35059
- const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
35060
- const noteW = Math.min(
35061
- maxW,
35062
- Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
35063
- );
35064
- const rightOffset = afterSelfCall && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
35065
- const noteX = isRight ? px + rightOffset : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
35066
- 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));
35067
- noteG.append("path").attr(
35068
- "d",
35069
- [
35070
- `M ${noteX} ${noteTopY}`,
35071
- `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
35072
- `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
35073
- `L ${noteX + noteW} ${noteTopY + noteH}`,
35074
- `L ${noteX} ${noteTopY + noteH}`,
35075
- "Z"
35076
- ].join(" ")
35077
- ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
35078
- noteG.append("path").attr(
35079
- "d",
35080
- [
35081
- `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
35082
- `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
35083
- `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
35084
- ].join(" ")
35085
- ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
35086
- wrappedLines.forEach((line11, li) => {
35087
- const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
35088
- const isBullet = line11.startsWith("- ");
35089
- const bulletIndent = isBullet ? 10 : 0;
35090
- const displayLine = isBullet ? line11.slice(2) : line11;
35091
- 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");
35092
- if (isBullet) {
35093
- noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
35094
- }
35095
- renderInlineText(textEl, displayLine, palette, NOTE_FONT_SIZE);
35096
- });
35097
- } else {
35098
- const cFold = 6;
35099
- const afterSelfCallC = isNoteAfterSelfCall(el);
35100
- const rightOffsetC = afterSelfCallC && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
35101
- const noteX = isRight ? px + rightOffsetC : px - ACTIVATION_WIDTH - NOTE_GAP - COLLAPSED_NOTE_W;
35102
- 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");
35103
- noteG.append("path").attr(
35104
- "d",
35105
- [
35106
- `M ${noteX} ${noteTopY}`,
35107
- `L ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY}`,
35108
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + cFold}`,
35109
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + COLLAPSED_NOTE_H}`,
35110
- `L ${noteX} ${noteTopY + COLLAPSED_NOTE_H}`,
35111
- "Z"
35112
- ].join(" ")
35113
- ).attr("fill", collapsedNoteFill).attr("stroke", palette.border).attr("stroke-width", 0.75).attr("class", "note-box");
35114
- noteG.append("path").attr(
35115
- "d",
35116
- [
35117
- `M ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY}`,
35118
- `L ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY + cFold}`,
35119
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + cFold}`
35120
- ].join(" ")
35121
- ).attr("fill", "none").attr("stroke", palette.border).attr("stroke-width", 0.75).attr("class", "note-fold");
35122
- 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");
35123
- }
36235
+ const afterSelfCall = isNoteAfterSelfCall(el);
36236
+ const maxW = noteEffectiveMaxW(
36237
+ el.participantId,
36238
+ el.position,
36239
+ afterSelfCall
36240
+ );
36241
+ const maxChars = charsForWidth(maxW);
36242
+ const wrappedLines = wrapTextLines(el.text, maxChars);
36243
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
36244
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
36245
+ const noteW = Math.min(
36246
+ maxW,
36247
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
36248
+ );
36249
+ const rightOffset = afterSelfCall && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
36250
+ const noteX = isRight ? px + rightOffset : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
36251
+ 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));
36252
+ noteG.append("path").attr(
36253
+ "d",
36254
+ [
36255
+ `M ${noteX} ${noteTopY}`,
36256
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
36257
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
36258
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
36259
+ `L ${noteX} ${noteTopY + noteH}`,
36260
+ "Z"
36261
+ ].join(" ")
36262
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
36263
+ noteG.append("path").attr(
36264
+ "d",
36265
+ [
36266
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
36267
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
36268
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
36269
+ ].join(" ")
36270
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
36271
+ wrappedLines.forEach((line11, li) => {
36272
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
36273
+ const isBullet = line11.startsWith("- ");
36274
+ const bulletIndent = isBullet ? 10 : 0;
36275
+ const displayLine = isBullet ? line11.slice(2) : line11;
36276
+ 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");
36277
+ if (isBullet) {
36278
+ noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
36279
+ }
36280
+ renderInlineText(textEl, displayLine, palette, NOTE_FONT_SIZE);
36281
+ });
35124
36282
  } else if (isSequenceBlock(el)) {
35125
36283
  renderNoteElements(el.children);
35126
36284
  if (el.elseIfBranches) {
@@ -35135,8 +36293,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35135
36293
  if (elements && elements.length > 0) {
35136
36294
  renderNoteElements(elements);
35137
36295
  }
35138
- if (hasTagGroups || showNotesControl) {
35139
- const controlsExpanded = options?.controlsExpanded ?? false;
36296
+ if (hasTagGroups) {
35140
36297
  const legendY = TOP_MARGIN + titleOffset;
35141
36298
  const resolvedGroups = parsed.tagGroups.filter((tg) => tg.entries.length > 0).map((tg) => ({
35142
36299
  name: tg.name,
@@ -35145,37 +36302,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35145
36302
  color: e.color
35146
36303
  }))
35147
36304
  }));
35148
- const allExpanded = showNotesControl && (options?.expandAllNotes ?? false);
35149
- const controlsGroup = showNotesControl ? {
35150
- toggles: [
35151
- {
35152
- id: "expand-all-notes",
35153
- type: "toggle",
35154
- label: "Expand Notes",
35155
- active: allExpanded,
35156
- onToggle: () => {
35157
- }
35158
- }
35159
- ]
35160
- } : void 0;
35161
36305
  const legendConfig = {
35162
36306
  groups: resolvedGroups,
35163
36307
  position: { placement: "top-center", titleRelation: "below-title" },
35164
- mode: "fixed",
35165
- controlsGroup
36308
+ mode: "fixed"
35166
36309
  };
35167
36310
  const legendState = {
35168
36311
  activeGroup: activeTagGroup ?? null,
35169
- controlsExpanded
35170
- };
35171
- const legendCallbacks = {
35172
- onControlsExpand: () => {
35173
- options?.onToggleControlsExpand?.();
35174
- },
35175
- onControlsToggle: (_toggleId, active) => {
35176
- options?.onExpandAllNotes?.(active);
35177
- }
36312
+ controlsExpanded: false
35178
36313
  };
36314
+ const legendCallbacks = {};
35179
36315
  const legendG = svg.append("g").attr("class", "sequence-legend").attr("transform", `translate(0,${legendY})`);
35180
36316
  renderLegendD3(
35181
36317
  legendG,
@@ -35214,26 +36350,6 @@ function buildNoteMessageMap(elements) {
35214
36350
  walk(elements);
35215
36351
  return map;
35216
36352
  }
35217
- function collectNoteLineNumbers(elements) {
35218
- const result = [];
35219
- const walk = (els) => {
35220
- for (const el of els) {
35221
- if (isSequenceNote(el)) {
35222
- result.push(el.lineNumber);
35223
- } else if (isSequenceBlock(el)) {
35224
- walk(el.children);
35225
- if (el.elseIfBranches) {
35226
- for (const branch of el.elseIfBranches) {
35227
- walk(branch.children);
35228
- }
35229
- }
35230
- walk(el.elseChildren);
35231
- }
35232
- }
35233
- };
35234
- walk(elements);
35235
- return result;
35236
- }
35237
36353
  function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr) {
35238
36354
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
35239
36355
  if (tagAttr) {
@@ -35289,8 +36405,8 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
35289
36405
  });
35290
36406
  }
35291
36407
  }
35292
- var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, NOTE_LANE_MAX, LABEL_CHAR_WIDTH, LABEL_MAX_CHARS, fill, stroke, SW, W, H;
35293
- var init_renderer15 = __esm({
36408
+ var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, NOTE_LANE_MAX, LABEL_CHAR_WIDTH, LABEL_MAX_CHARS, fill, stroke, SW, W, H;
36409
+ var init_renderer16 = __esm({
35294
36410
  "src/sequence/renderer.ts"() {
35295
36411
  "use strict";
35296
36412
  init_color_utils();
@@ -35324,8 +36440,6 @@ var init_renderer15 = __esm({
35324
36440
  NOTE_CHARS_PER_LINE = Math.floor(
35325
36441
  (NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W
35326
36442
  );
35327
- COLLAPSED_NOTE_H = 20;
35328
- COLLAPSED_NOTE_W = 40;
35329
36443
  ACTIVATION_WIDTH = 10;
35330
36444
  SELF_CALL_HEIGHT = 25;
35331
36445
  SELF_CALL_WIDTH = 30;
@@ -35344,7 +36458,7 @@ var init_renderer15 = __esm({
35344
36458
 
35345
36459
  // src/d3.ts
35346
36460
  import * as d3Scale2 from "d3-scale";
35347
- import * as d3Selection18 from "d3-selection";
36461
+ import * as d3Selection19 from "d3-selection";
35348
36462
  import * as d3Shape10 from "d3-shape";
35349
36463
  import * as d3Array from "d3-array";
35350
36464
  import cloud from "d3-cloud";
@@ -35355,15 +36469,15 @@ function renderChartTitle(svg, title, titleLineNumber, width, textColor, onClick
35355
36469
  titleEl.attr("data-line-number", titleLineNumber);
35356
36470
  if (onClickItem) {
35357
36471
  titleEl.on("click", () => onClickItem(titleLineNumber)).on("mouseenter", function() {
35358
- d3Selection18.select(this).attr("opacity", 0.7);
36472
+ d3Selection19.select(this).attr("opacity", 0.7);
35359
36473
  }).on("mouseleave", function() {
35360
- d3Selection18.select(this).attr("opacity", 1);
36474
+ d3Selection19.select(this).attr("opacity", 1);
35361
36475
  });
35362
36476
  }
35363
36477
  }
35364
36478
  }
35365
36479
  function initD3Chart(container, palette, exportDims) {
35366
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
36480
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
35367
36481
  const width = exportDims?.width ?? container.clientWidth;
35368
36482
  const height = exportDims?.height ?? container.clientHeight;
35369
36483
  if (width <= 0 || height <= 0) return null;
@@ -35371,7 +36485,7 @@ function initD3Chart(container, palette, exportDims) {
35371
36485
  const mutedColor = palette.border;
35372
36486
  const bgColor = palette.bg;
35373
36487
  const colors = getSeriesColors(palette);
35374
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
36488
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
35375
36489
  return { svg, width, height, textColor, mutedColor, bgColor, colors };
35376
36490
  }
35377
36491
  function parseTimelineDate(s) {
@@ -36296,7 +37410,12 @@ function parseVisualization(content, palette) {
36296
37410
  );
36297
37411
  validateTagGroupNames(
36298
37412
  result.timelineTagGroups,
36299
- (line11, msg) => result.diagnostics.push(makeDgmoError(line11, msg, "warning"))
37413
+ (line11, msg) => result.diagnostics.push(makeDgmoError(line11, msg, "warning")),
37414
+ (line11, msg) => {
37415
+ const diag = makeDgmoError(line11, msg);
37416
+ result.diagnostics.push(diag);
37417
+ if (!result.error) result.error = formatDgmoError(diag);
37418
+ }
36300
37419
  );
36301
37420
  for (const group of result.timelineTagGroups) {
36302
37421
  if (!group.defaultValue) continue;
@@ -36714,14 +37833,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36714
37833
  function handleMouseEnter(hovered) {
36715
37834
  const connected = neighbors.get(hovered);
36716
37835
  g.selectAll(".arc-link").each(function() {
36717
- const el = d3Selection18.select(this);
37836
+ const el = d3Selection19.select(this);
36718
37837
  const src = el.attr("data-source");
36719
37838
  const tgt = el.attr("data-target");
36720
37839
  const isRelated = src === hovered || tgt === hovered;
36721
37840
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36722
37841
  });
36723
37842
  g.selectAll(".arc-node").each(function() {
36724
- const el = d3Selection18.select(this);
37843
+ const el = d3Selection19.select(this);
36725
37844
  const name = el.attr("data-node");
36726
37845
  const isRelated = name === hovered || connected.has(name);
36727
37846
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY2);
@@ -36746,23 +37865,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36746
37865
  const members = groupNodeSets.get(groupName);
36747
37866
  if (!members) return;
36748
37867
  g.selectAll(".arc-link").each(function() {
36749
- const el = d3Selection18.select(this);
37868
+ const el = d3Selection19.select(this);
36750
37869
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
36751
37870
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36752
37871
  });
36753
37872
  g.selectAll(".arc-node").each(function() {
36754
- const el = d3Selection18.select(this);
37873
+ const el = d3Selection19.select(this);
36755
37874
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY2);
36756
37875
  });
36757
37876
  g.selectAll(".arc-group-band").each(function() {
36758
- const el = d3Selection18.select(this);
37877
+ const el = d3Selection19.select(this);
36759
37878
  el.attr(
36760
37879
  "fill-opacity",
36761
37880
  el.attr("data-group") === groupName ? 0.18 : 0.03
36762
37881
  );
36763
37882
  });
36764
37883
  g.selectAll(".arc-group-label").each(function() {
36765
- const el = d3Selection18.select(this);
37884
+ const el = d3Selection19.select(this);
36766
37885
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
36767
37886
  });
36768
37887
  }
@@ -37020,7 +38139,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
37020
38139
  function hideEventDatesOnScale(g) {
37021
38140
  g.selectAll(".tl-event-date").remove();
37022
38141
  g.selectAll(".tl-scale-tick").each(function() {
37023
- const el = d3Selection18.select(this);
38142
+ const el = d3Selection19.select(this);
37024
38143
  const isDashed = el.attr("stroke-dasharray");
37025
38144
  el.attr("opacity", isDashed ? 0.15 : 0.4);
37026
38145
  });
@@ -37098,7 +38217,7 @@ function renderTimelineGroupLegend(g, groups, groupColorMap, textColor, palette,
37098
38217
  }
37099
38218
  }
37100
38219
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange, viewMode) {
37101
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
38220
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
37102
38221
  const {
37103
38222
  timelineEvents,
37104
38223
  timelineGroups,
@@ -37196,13 +38315,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37196
38315
  const FADE_OPACITY2 = 0.1;
37197
38316
  function fadeToGroup(g, groupName) {
37198
38317
  g.selectAll(".tl-event").each(function() {
37199
- const el = d3Selection18.select(this);
38318
+ const el = d3Selection19.select(this);
37200
38319
  const evGroup = el.attr("data-group");
37201
38320
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY2);
37202
38321
  });
37203
38322
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
37204
38323
  function() {
37205
- const el = d3Selection18.select(this);
38324
+ const el = d3Selection19.select(this);
37206
38325
  const name = el.attr("data-group");
37207
38326
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY2);
37208
38327
  }
@@ -37214,7 +38333,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37214
38333
  }
37215
38334
  function fadeToEra(g, eraStart, eraEnd) {
37216
38335
  g.selectAll(".tl-event").each(function() {
37217
- const el = d3Selection18.select(this);
38336
+ const el = d3Selection19.select(this);
37218
38337
  const date = parseFloat(el.attr("data-date"));
37219
38338
  const endDate = el.attr("data-end-date");
37220
38339
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -37226,14 +38345,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37226
38345
  FADE_OPACITY2
37227
38346
  );
37228
38347
  g.selectAll(".tl-era").each(function() {
37229
- const el = d3Selection18.select(this);
38348
+ const el = d3Selection19.select(this);
37230
38349
  const s = parseFloat(el.attr("data-era-start"));
37231
38350
  const e = parseFloat(el.attr("data-era-end"));
37232
38351
  const isSelf = s === eraStart && e === eraEnd;
37233
38352
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY2);
37234
38353
  });
37235
38354
  g.selectAll(".tl-marker").each(function() {
37236
- const el = d3Selection18.select(this);
38355
+ const el = d3Selection19.select(this);
37237
38356
  const date = parseFloat(el.attr("data-marker-date"));
37238
38357
  const inside = date >= eraStart && date <= eraEnd;
37239
38358
  el.attr("opacity", inside ? 1 : FADE_OPACITY2);
@@ -37248,7 +38367,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37248
38367
  function fadeToTagValue(g, tagKey, tagValue) {
37249
38368
  const attrName = `data-tag-${tagKey}`;
37250
38369
  g.selectAll(".tl-event").each(function() {
37251
- const el = d3Selection18.select(this);
38370
+ const el = d3Selection19.select(this);
37252
38371
  const val = el.attr(attrName);
37253
38372
  el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY2);
37254
38373
  });
@@ -37261,7 +38380,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37261
38380
  FADE_OPACITY2
37262
38381
  );
37263
38382
  g.selectAll(".tl-tag-legend-entry").each(function() {
37264
- const el = d3Selection18.select(this);
38383
+ const el = d3Selection19.select(this);
37265
38384
  const entryValue = el.attr("data-legend-entry");
37266
38385
  if (entryValue === "__group__") return;
37267
38386
  const entryGroup = el.attr("data-tag-group");
@@ -37313,7 +38432,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37313
38432
  const innerHeight = height - margin.top - margin.bottom;
37314
38433
  const laneWidth = innerWidth / laneCount;
37315
38434
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37316
- const svg = d3Selection18.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
38435
+ const svg = d3Selection19.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
37317
38436
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37318
38437
  renderChartTitle(
37319
38438
  svg,
@@ -37403,7 +38522,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37403
38522
  const gradientId = `uncertain-vg-${ev.lineNumber}`;
37404
38523
  const strokeGradientId = `uncertain-vg-s-${ev.lineNumber}`;
37405
38524
  const defs = svg.select("defs").node() || svg.append("defs").node();
37406
- const defsEl = d3Selection18.select(defs);
38525
+ const defsEl = d3Selection19.select(defs);
37407
38526
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37408
38527
  { offset: "0%", opacity: 1 },
37409
38528
  { offset: "80%", opacity: 1 },
@@ -37439,7 +38558,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37439
38558
  const axisX = 20;
37440
38559
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37441
38560
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
37442
- const svg = d3Selection18.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
38561
+ const svg = d3Selection19.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
37443
38562
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37444
38563
  renderChartTitle(
37445
38564
  svg,
@@ -37528,7 +38647,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37528
38647
  const gradientId = `uncertain-v-${ev.lineNumber}`;
37529
38648
  const strokeGradientId = `uncertain-v-s-${ev.lineNumber}`;
37530
38649
  const defs = svg.select("defs").node() || svg.append("defs").node();
37531
- const defsEl = d3Selection18.select(defs);
38650
+ const defsEl = d3Selection19.select(defs);
37532
38651
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37533
38652
  { offset: "0%", opacity: 1 },
37534
38653
  { offset: "80%", opacity: 1 },
@@ -37596,7 +38715,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37596
38715
  const totalGaps = (lanes.length - 1) * GROUP_GAP3;
37597
38716
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
37598
38717
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37599
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38718
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37600
38719
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37601
38720
  renderChartTitle(
37602
38721
  svg,
@@ -37709,7 +38828,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37709
38828
  const gradientId = `uncertain-${ev.lineNumber}`;
37710
38829
  const strokeGradientId = `uncertain-s-${ev.lineNumber}`;
37711
38830
  const defs = svg.select("defs").node() || svg.append("defs").node();
37712
- const defsEl = d3Selection18.select(defs);
38831
+ const defsEl = d3Selection19.select(defs);
37713
38832
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37714
38833
  { offset: "0%", opacity: 1 },
37715
38834
  { offset: "80%", opacity: 1 },
@@ -37757,7 +38876,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37757
38876
  const innerHeight = height - margin.top - margin.bottom;
37758
38877
  const rowH = Math.min(28, innerHeight / sorted.length);
37759
38878
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37760
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38879
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37761
38880
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37762
38881
  renderChartTitle(
37763
38882
  svg,
@@ -37866,7 +38985,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37866
38985
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
37867
38986
  const strokeGradientId = `uncertain-ts-s-${ev.lineNumber}`;
37868
38987
  const defs = svg.select("defs").node() || svg.append("defs").node();
37869
- const defsEl = d3Selection18.select(defs);
38988
+ const defsEl = d3Selection19.select(defs);
37870
38989
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37871
38990
  { offset: "0%", opacity: 1 },
37872
38991
  { offset: "80%", opacity: 1 },
@@ -37909,7 +39028,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37909
39028
  const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
37910
39029
  const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
37911
39030
  const LG_ICON_W = 20;
37912
- const mainSvg = d3Selection18.select(container).select("svg");
39031
+ const mainSvg = d3Selection19.select(container).select("svg");
37913
39032
  const mainG = mainSvg.select("g");
37914
39033
  if (!mainSvg.empty() && !mainG.empty()) {
37915
39034
  let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
@@ -37982,7 +39101,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37982
39101
  const tagVal = entryValue.toLowerCase();
37983
39102
  fadeToTagValue(mainG, tagKey, tagVal);
37984
39103
  mainSvg.selectAll("[data-legend-entry]").each(function() {
37985
- const el = d3Selection18.select(this);
39104
+ const el = d3Selection19.select(this);
37986
39105
  const ev = el.attr("data-legend-entry");
37987
39106
  const eg = el.attr("data-tag-group") ?? el.node()?.closest?.("[data-tag-group]")?.getAttribute("data-tag-group");
37988
39107
  el.attr(
@@ -38035,7 +39154,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
38035
39154
  }, recolorEvents2 = function() {
38036
39155
  const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
38037
39156
  mainG.selectAll(".tl-event").each(function() {
38038
- const el = d3Selection18.select(this);
39157
+ const el = d3Selection19.select(this);
38039
39158
  const lineNum = el.attr("data-line-number");
38040
39159
  const ev = lineNum ? eventByLine.get(lineNum) : void 0;
38041
39160
  if (!ev) return;
@@ -38133,7 +39252,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
38133
39252
  }
38134
39253
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38135
39254
  return new Promise((resolve) => {
38136
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
39255
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
38137
39256
  const { words, title, cloudOptions } = parsed;
38138
39257
  if (words.length === 0) {
38139
39258
  resolve();
@@ -38160,7 +39279,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38160
39279
  return minSize + Math.sqrt(t) * (maxSize - minSize);
38161
39280
  };
38162
39281
  const rotateFn = getRotateFn(cloudOptions.rotate);
38163
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
39282
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38164
39283
  renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
38165
39284
  const g = svg.append("g").attr(
38166
39285
  "transform",
@@ -38662,8 +39781,8 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38662
39781
  const LABEL_MAX_FONT = 48;
38663
39782
  const LABEL_MIN_FONT = 14;
38664
39783
  const LABEL_PAD3 = 40;
38665
- const CHAR_WIDTH_RATIO4 = 0.6;
38666
- const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO4;
39784
+ const CHAR_WIDTH_RATIO5 = 0.6;
39785
+ const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO5;
38667
39786
  const quadrantLabelLayout = (text, qw2, qh2) => {
38668
39787
  const availW = qw2 - LABEL_PAD3;
38669
39788
  const availH = qh2 - LABEL_PAD3;
@@ -38726,7 +39845,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38726
39845
  (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
38727
39846
  ).each(function(d) {
38728
39847
  const layout = labelLayouts.get(d.label.text);
38729
- const el = d3Selection18.select(this);
39848
+ const el = d3Selection19.select(this);
38730
39849
  if (layout.lines.length === 1) {
38731
39850
  el.text(layout.lines[0]);
38732
39851
  } else {
@@ -38742,9 +39861,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38742
39861
  quadrantLabelTexts.on("click", (_, d) => {
38743
39862
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
38744
39863
  }).on("mouseenter", function() {
38745
- d3Selection18.select(this).attr("opacity", 0.7);
39864
+ d3Selection19.select(this).attr("opacity", 0.7);
38746
39865
  }).on("mouseleave", function() {
38747
- d3Selection18.select(this).attr("opacity", 1);
39866
+ d3Selection19.select(this).attr("opacity", 1);
38748
39867
  });
38749
39868
  }
38750
39869
  if (quadrantXAxis) {
@@ -38765,9 +39884,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38765
39884
  if (onClickItem && quadrantXAxisLineNumber) {
38766
39885
  [xLowLabel, xHighLabel].forEach((label) => {
38767
39886
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
38768
- d3Selection18.select(this).attr("opacity", 0.7);
39887
+ d3Selection19.select(this).attr("opacity", 0.7);
38769
39888
  }).on("mouseleave", function() {
38770
- d3Selection18.select(this).attr("opacity", 1);
39889
+ d3Selection19.select(this).attr("opacity", 1);
38771
39890
  });
38772
39891
  });
38773
39892
  }
@@ -38792,9 +39911,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38792
39911
  if (onClickItem && quadrantYAxisLineNumber) {
38793
39912
  [yLowLabel, yHighLabel].forEach((label) => {
38794
39913
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
38795
- d3Selection18.select(this).attr("opacity", 0.7);
39914
+ d3Selection19.select(this).attr("opacity", 0.7);
38796
39915
  }).on("mouseleave", function() {
38797
- d3Selection18.select(this).attr("opacity", 1);
39916
+ d3Selection19.select(this).attr("opacity", 1);
38798
39917
  });
38799
39918
  });
38800
39919
  }
@@ -38811,7 +39930,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38811
39930
  const POINT_LABEL_FONT_SIZE = 12;
38812
39931
  const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
38813
39932
  const layout = labelLayouts.get(d.label.text);
38814
- const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO4;
39933
+ const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO5;
38815
39934
  const totalH = layout.lines.length * layout.fontSize * 1.2;
38816
39935
  return {
38817
39936
  x: d.labelX - totalW / 2,
@@ -38871,7 +39990,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38871
39990
  pointsG.selectAll("g.point-group").each(function(_2, i) {
38872
39991
  const pt = quadrantPoints[i];
38873
39992
  const ptQuad = getPointQuadrant(pt.x, pt.y);
38874
- d3Selection18.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
39993
+ d3Selection19.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
38875
39994
  });
38876
39995
  }).on("mouseleave", () => {
38877
39996
  quadrantRects.attr("opacity", 1);
@@ -39390,6 +40509,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
39390
40509
  );
39391
40510
  return finalizeSvgExport(container2, theme, effectivePalette2);
39392
40511
  }
40512
+ if (detectedType === "pyramid") {
40513
+ const { parsePyramid: parsePyramid2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports16));
40514
+ const { renderPyramidForExport: renderPyramidForExport2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40515
+ const effectivePalette2 = await resolveExportPalette(theme, palette);
40516
+ const pyramidParsed = parsePyramid2(content);
40517
+ if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
40518
+ const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
40519
+ renderPyramidForExport2(
40520
+ container2,
40521
+ pyramidParsed,
40522
+ effectivePalette2,
40523
+ theme === "dark",
40524
+ { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
40525
+ );
40526
+ return finalizeSvgExport(container2, theme, effectivePalette2);
40527
+ }
39393
40528
  const parsed = parseVisualization(content, palette);
39394
40529
  if (parsed.error && parsed.type !== "sequence") {
39395
40530
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -39413,7 +40548,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
39413
40548
  };
39414
40549
  if (parsed.type === "sequence") {
39415
40550
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
39416
- const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40551
+ const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
39417
40552
  const seqParsed = parseSequenceDgmo2(content);
39418
40553
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
39419
40554
  renderSequenceDiagram2(
@@ -40120,8 +41255,7 @@ async function render(content, options) {
40120
41255
  const theme = options?.theme ?? "light";
40121
41256
  const paletteName = options?.palette ?? "nord";
40122
41257
  const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
40123
- const { diagnostics } = parseDgmo(content);
40124
- const chartType = parseDgmoChartType(content);
41258
+ const { diagnostics, chartType } = parseDgmo(content);
40125
41259
  const category = chartType ? getRenderCategory(chartType) : null;
40126
41260
  const viewState = options?.viewState ?? (options?.legendState ? {
40127
41261
  tag: options.legendState.activeGroup ?? void 0,
@@ -40145,6 +41279,83 @@ async function render(content, options) {
40145
41279
  return { svg, diagnostics };
40146
41280
  }
40147
41281
 
41282
+ // src/index.ts
41283
+ init_chart_types();
41284
+
41285
+ // src/chart-type-scoring.ts
41286
+ init_chart_types();
41287
+ var TYPOGRAPHIC_REPLACEMENTS = [
41288
+ [/[‘’]/g, "'"],
41289
+ // curly single quotes
41290
+ [/[“”]/g, '"'],
41291
+ // curly double quotes
41292
+ [/[–—]/g, "-"],
41293
+ // en/em dash
41294
+ [/×/g, "x"]
41295
+ // unicode multiplication → ASCII (for "2×2" vs "2x2")
41296
+ ];
41297
+ function normalize(s) {
41298
+ let out = s.normalize("NFKD").toLowerCase();
41299
+ for (const [re, repl] of TYPOGRAPHIC_REPLACEMENTS)
41300
+ out = out.replace(re, repl);
41301
+ return out.split(/[^a-z0-9]+/).filter(Boolean);
41302
+ }
41303
+ function matchesContiguously(promptTokens, triggerTokens) {
41304
+ if (triggerTokens.length === 0 || triggerTokens.length > promptTokens.length)
41305
+ return false;
41306
+ outer: for (let i = 0; i <= promptTokens.length - triggerTokens.length; i++) {
41307
+ for (let j = 0; j < triggerTokens.length; j++) {
41308
+ if (promptTokens[i + j] !== triggerTokens[j]) continue outer;
41309
+ }
41310
+ return true;
41311
+ }
41312
+ return false;
41313
+ }
41314
+ function scoreChartType(prompt, type) {
41315
+ const promptTokens = normalize(prompt);
41316
+ const matched = [];
41317
+ let score = 0;
41318
+ for (const trigger of type.triggers) {
41319
+ const triggerTokens = normalize(trigger);
41320
+ if (matchesContiguously(promptTokens, triggerTokens)) {
41321
+ matched.push(trigger);
41322
+ score += triggerTokens.length;
41323
+ }
41324
+ }
41325
+ const descTokens = new Set(normalize(type.description));
41326
+ let descHits = 0;
41327
+ for (const t of promptTokens) if (descTokens.has(t)) descHits++;
41328
+ score += descHits * 0.25;
41329
+ return { score, matched };
41330
+ }
41331
+ var MIN_PRIMARY_SCORE = 1;
41332
+ var AMBIGUITY_THRESHOLD = 0.5;
41333
+ function confidence(top, second) {
41334
+ if (top < MIN_PRIMARY_SCORE) return "ambiguous";
41335
+ if (second === 0) return "high";
41336
+ if (top >= second * 2) return "high";
41337
+ if (top - second < AMBIGUITY_THRESHOLD) return "ambiguous";
41338
+ return "medium";
41339
+ }
41340
+ function suggestChartTypes(prompt) {
41341
+ const scored = [];
41342
+ for (const type of chartTypes) {
41343
+ const { score, matched } = scoreChartType(prompt, type);
41344
+ if (score > 0) scored.push({ type, score, matched });
41345
+ }
41346
+ scored.sort((a, b) => b.score - a.score);
41347
+ const fallback = chartTypes.filter((c) => c.fallback);
41348
+ const topScore = scored[0]?.score ?? 0;
41349
+ const secondScore = scored[1]?.score ?? 0;
41350
+ const fellBack = topScore < MIN_PRIMARY_SCORE;
41351
+ return {
41352
+ ranked: scored,
41353
+ fallback,
41354
+ confidence: confidence(topScore, secondScore),
41355
+ fellBack
41356
+ };
41357
+ }
41358
+
40148
41359
  // src/index.ts
40149
41360
  init_dgmo_router();
40150
41361
  init_chart();
@@ -40442,6 +41653,8 @@ init_renderer14();
40442
41653
  init_parser15();
40443
41654
  init_layout12();
40444
41655
  init_renderer13();
41656
+ init_parser16();
41657
+ init_renderer15();
40445
41658
 
40446
41659
  // src/org/resolver.ts
40447
41660
  init_diagnostics();
@@ -40777,7 +41990,7 @@ init_legend_constants();
40777
41990
  init_legend_d3();
40778
41991
  init_legend_layout();
40779
41992
  init_d3();
40780
- init_renderer15();
41993
+ init_renderer16();
40781
41994
  init_collapse4();
40782
41995
  init_colors();
40783
41996
  init_palettes();
@@ -40880,6 +42093,7 @@ init_flowchart_parser();
40880
42093
  init_parser8();
40881
42094
  init_parser2();
40882
42095
  init_parsing();
42096
+ init_dgmo_router();
40883
42097
  var extractorRegistry = /* @__PURE__ */ new Map();
40884
42098
  function registerExtractor(kind, fn) {
40885
42099
  extractorRegistry.set(kind, fn);
@@ -41053,10 +42267,6 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41053
42267
  description: "Show activation bars",
41054
42268
  values: ["on", "off"]
41055
42269
  },
41056
- "collapse-notes": {
41057
- description: "Collapse note blocks",
41058
- values: ["yes", "no"]
41059
- },
41060
42270
  "active-tag": { description: "Active tag group name" }
41061
42271
  })
41062
42272
  ],
@@ -41195,51 +42405,16 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41195
42405
  "no-legend": { description: "Hide the score legend" },
41196
42406
  persona: { description: "Define the journey persona" }
41197
42407
  })
42408
+ ],
42409
+ [
42410
+ "pyramid",
42411
+ withGlobals({
42412
+ inverted: { description: "Flip apex to the bottom (funnel orientation)" },
42413
+ color: { description: "Override layer color (pipe metadata)" },
42414
+ description: { description: "Layer description (pipe or indented body)" }
42415
+ })
41198
42416
  ]
41199
42417
  ]);
41200
- var CHART_TYPE_DESCRIPTIONS = {
41201
- // Data charts
41202
- bar: "Bar chart",
41203
- line: "Line chart",
41204
- pie: "Pie chart",
41205
- doughnut: "Doughnut chart",
41206
- area: "Area chart",
41207
- "polar-area": "Polar area chart",
41208
- radar: "Radar chart",
41209
- "bar-stacked": "Stacked bar chart",
41210
- // Extended charts
41211
- scatter: "Scatter plot",
41212
- heatmap: "Heatmap",
41213
- sankey: "Sankey flow diagram",
41214
- chord: "Chord diagram",
41215
- funnel: "Funnel chart",
41216
- function: "Mathematical function plot",
41217
- // Visualizations
41218
- slope: "Slope chart",
41219
- wordcloud: "Word cloud",
41220
- arc: "Arc diagram",
41221
- timeline: "Timeline",
41222
- venn: "Venn diagram",
41223
- quadrant: "Quadrant chart",
41224
- // Diagrams
41225
- sequence: "Sequence diagram",
41226
- flowchart: "Flowchart",
41227
- class: "Class diagram",
41228
- er: "Entity-relationship diagram",
41229
- org: "Organization chart",
41230
- kanban: "Kanban board",
41231
- c4: "C4 architecture diagram",
41232
- state: "State diagram",
41233
- sitemap: "Sitemap diagram",
41234
- infra: "Infrastructure diagram",
41235
- gantt: "Gantt chart",
41236
- "boxes-and-lines": "Boxes and lines diagram",
41237
- mindmap: "Mindmap diagram",
41238
- wireframe: "UI wireframe diagram",
41239
- "tech-radar": "Technology adoption radar (ThoughtWorks style)",
41240
- cycle: "Cycle diagram (circular process flow)",
41241
- "journey-map": "User journey map with emotion curve"
41242
- };
41243
42418
  var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((name) => ({
41244
42419
  name,
41245
42420
  description: CHART_TYPE_DESCRIPTIONS[name] ?? name
@@ -41828,13 +43003,16 @@ function extractJourneyMapSymbols(docText) {
41828
43003
  init_parsing();
41829
43004
  export {
41830
43005
  ALL_CHART_TYPES,
43006
+ AMBIGUITY_THRESHOLD,
41831
43007
  ARROW_DIAGNOSTIC_CODES,
41832
43008
  CHART_TYPES,
43009
+ CHART_TYPE_DESCRIPTIONS,
41833
43010
  COMPLETION_REGISTRY,
41834
43011
  ENTITY_TYPES,
41835
43012
  INFRA_BEHAVIOR_KEYS,
41836
43013
  LEGEND_HEIGHT,
41837
43014
  METADATA_KEY_SET,
43015
+ MIN_PRIMARY_SCORE,
41838
43016
  PIPE_METADATA,
41839
43017
  RECOGNIZED_COLOR_NAMES,
41840
43018
  RULE_COUNT,
@@ -41850,13 +43028,15 @@ export {
41850
43028
  buildTagLaneRowList,
41851
43029
  calculateSchedule,
41852
43030
  catppuccinPalette,
43031
+ confidence as chartTypeConfidence,
43032
+ chartTypeParsers,
43033
+ chartTypes,
41853
43034
  collapseBoxesAndLines,
41854
43035
  collapseMindmapTree,
41855
43036
  collapseOrgTree,
41856
43037
  collapseSitemapTree,
41857
43038
  collapseStateGroups,
41858
43039
  collectDiagramRoles,
41859
- collectNoteLineNumbers,
41860
43040
  collectTasks,
41861
43041
  colorNames,
41862
43042
  computeActivations,
@@ -41877,8 +43057,10 @@ export {
41877
43057
  encodeViewState,
41878
43058
  extractDiagramSymbols,
41879
43059
  extractTagDeclarations,
43060
+ focusOrgTree,
41880
43061
  formatDateLabel,
41881
43062
  formatDgmoError,
43063
+ getAllChartTypes,
41882
43064
  getAvailablePalettes,
41883
43065
  getExtendedChartLegendGroups,
41884
43066
  getLegendReservedHeight,
@@ -41900,6 +43082,7 @@ export {
41900
43082
  isSequenceBlock,
41901
43083
  isSequenceNote,
41902
43084
  isValidHex,
43085
+ knownChartTypeIds,
41903
43086
  layoutBoxesAndLines,
41904
43087
  layoutC4Components,
41905
43088
  layoutC4Containers,
@@ -41922,10 +43105,12 @@ export {
41922
43105
  looksLikeState,
41923
43106
  makeDgmoError,
41924
43107
  matchColorParens,
43108
+ matchesContiguously,
41925
43109
  mix,
41926
43110
  monokaiPalette,
41927
43111
  nord,
41928
43112
  nordPalette,
43113
+ normalize as normalizeChartTypePrompt,
41929
43114
  oneDarkPalette,
41930
43115
  orderArcNodes,
41931
43116
  parseAndLayoutInfra,
@@ -41949,7 +43134,9 @@ export {
41949
43134
  parseKanban,
41950
43135
  parseMindmap,
41951
43136
  parseOrg,
43137
+ parsePyramid,
41952
43138
  parseSequenceDgmo,
43139
+ parseSequenceDgmo as parseSequenceDiagram,
41953
43140
  parseSitemap,
41954
43141
  parseState,
41955
43142
  parseTechRadar,
@@ -41992,6 +43179,8 @@ export {
41992
43179
  renderMindmapForExport,
41993
43180
  renderOrg,
41994
43181
  renderOrgForExport,
43182
+ renderPyramid,
43183
+ renderPyramidForExport,
41995
43184
  renderQuadrant,
41996
43185
  renderQuadrantFocus,
41997
43186
  renderQuadrantFocusForExport,
@@ -42013,9 +43202,11 @@ export {
42013
43202
  resolveTaskName,
42014
43203
  rollUpContextRelationships,
42015
43204
  rosePinePalette,
43205
+ scoreChartType,
42016
43206
  seriesColors,
42017
43207
  shade,
42018
43208
  solarizedPalette,
43209
+ suggestChartTypes,
42019
43210
  tint,
42020
43211
  tokyoNightPalette,
42021
43212
  truncateBareUrl,