@diagrammo/dgmo 0.8.10 → 0.8.11

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.
package/dist/index.js CHANGED
@@ -122,6 +122,183 @@ var init_branding = __esm({
122
122
  }
123
123
  });
124
124
 
125
+ // src/label-layout.ts
126
+ function rectsOverlap(a, b) {
127
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
128
+ }
129
+ function rectCircleOverlap(rect, circle) {
130
+ const nearestX = Math.max(rect.x, Math.min(circle.cx, rect.x + rect.w));
131
+ const nearestY = Math.max(rect.y, Math.min(circle.cy, rect.y + rect.h));
132
+ const dx = nearestX - circle.cx;
133
+ const dy = nearestY - circle.cy;
134
+ return dx * dx + dy * dy < circle.r * circle.r;
135
+ }
136
+ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius, fontSize) {
137
+ const labelHeight = fontSize + 4;
138
+ const stepSize = labelHeight + 2;
139
+ const minGap = pointRadius + 4;
140
+ const pointCircles = points.map((p) => ({
141
+ cx: p.cx,
142
+ cy: p.cy,
143
+ r: pointRadius
144
+ }));
145
+ const placedLabels = [];
146
+ const results = [];
147
+ for (let i = 0; i < points.length; i++) {
148
+ const pt = points[i];
149
+ const labelWidth = pt.label.length * fontSize * CHAR_WIDTH_RATIO + 8;
150
+ let best = null;
151
+ const directions = [
152
+ {
153
+ // Above
154
+ gen: (offset) => {
155
+ const lx = pt.cx - labelWidth / 2;
156
+ const ly = pt.cy - offset - labelHeight;
157
+ if (ly < chartBounds.top || lx < chartBounds.left || lx + labelWidth > chartBounds.right)
158
+ return null;
159
+ return {
160
+ rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
161
+ textX: pt.cx,
162
+ textY: ly + labelHeight / 2,
163
+ anchor: "middle"
164
+ };
165
+ }
166
+ },
167
+ {
168
+ // Below
169
+ gen: (offset) => {
170
+ const lx = pt.cx - labelWidth / 2;
171
+ const ly = pt.cy + offset;
172
+ if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx + labelWidth > chartBounds.right)
173
+ return null;
174
+ return {
175
+ rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
176
+ textX: pt.cx,
177
+ textY: ly + labelHeight / 2,
178
+ anchor: "middle"
179
+ };
180
+ }
181
+ },
182
+ {
183
+ // Right
184
+ gen: (offset) => {
185
+ const lx = pt.cx + offset;
186
+ const ly = pt.cy - labelHeight / 2;
187
+ if (lx + labelWidth > chartBounds.right || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
188
+ return null;
189
+ return {
190
+ rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
191
+ textX: lx,
192
+ textY: pt.cy,
193
+ anchor: "start"
194
+ };
195
+ }
196
+ },
197
+ {
198
+ // Left
199
+ gen: (offset) => {
200
+ const lx = pt.cx - offset - labelWidth;
201
+ const ly = pt.cy - labelHeight / 2;
202
+ if (lx < chartBounds.left || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
203
+ return null;
204
+ return {
205
+ rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
206
+ textX: lx + labelWidth,
207
+ textY: pt.cy,
208
+ anchor: "end"
209
+ };
210
+ }
211
+ }
212
+ ];
213
+ for (const { gen } of directions) {
214
+ for (let offset = minGap; ; offset += stepSize) {
215
+ const cand = gen(offset);
216
+ if (!cand) break;
217
+ let collision = false;
218
+ for (const pl of placedLabels) {
219
+ if (rectsOverlap(cand.rect, pl)) {
220
+ collision = true;
221
+ break;
222
+ }
223
+ }
224
+ if (!collision) {
225
+ for (const circle of pointCircles) {
226
+ if (rectCircleOverlap(cand.rect, circle)) {
227
+ collision = true;
228
+ break;
229
+ }
230
+ }
231
+ }
232
+ if (!collision) {
233
+ for (const obs of obstacles) {
234
+ if (rectsOverlap(cand.rect, obs)) {
235
+ collision = true;
236
+ break;
237
+ }
238
+ }
239
+ }
240
+ if (!collision) {
241
+ const dist = offset;
242
+ if (!best || dist < best.dist) {
243
+ best = {
244
+ rect: cand.rect,
245
+ textX: cand.textX,
246
+ textY: cand.textY,
247
+ anchor: cand.anchor,
248
+ dist
249
+ };
250
+ }
251
+ break;
252
+ }
253
+ }
254
+ }
255
+ if (!best) {
256
+ const lx = pt.cx - labelWidth / 2;
257
+ const ly = pt.cy - minGap - labelHeight;
258
+ best = {
259
+ rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
260
+ textX: pt.cx,
261
+ textY: ly + labelHeight / 2,
262
+ anchor: "middle",
263
+ dist: minGap
264
+ };
265
+ }
266
+ placedLabels.push(best.rect);
267
+ let connectorLine;
268
+ if (best.dist > minGap + stepSize) {
269
+ const dx = best.textX - pt.cx;
270
+ const dy = best.textY - pt.cy;
271
+ const angle = Math.atan2(dy, dx);
272
+ const x1 = pt.cx + Math.cos(angle) * pointRadius;
273
+ const y1 = pt.cy + Math.sin(angle) * pointRadius;
274
+ const x2 = Math.max(
275
+ best.rect.x,
276
+ Math.min(pt.cx, best.rect.x + best.rect.w)
277
+ );
278
+ const y2 = Math.max(
279
+ best.rect.y,
280
+ Math.min(pt.cy, best.rect.y + best.rect.h)
281
+ );
282
+ connectorLine = { x1, y1, x2, y2 };
283
+ }
284
+ results.push({
285
+ label: pt.label,
286
+ x: best.textX,
287
+ y: best.textY,
288
+ anchor: best.anchor,
289
+ connectorLine
290
+ });
291
+ }
292
+ return results;
293
+ }
294
+ var CHAR_WIDTH_RATIO;
295
+ var init_label_layout = __esm({
296
+ "src/label-layout.ts"() {
297
+ "use strict";
298
+ CHAR_WIDTH_RATIO = 0.6;
299
+ }
300
+ });
301
+
125
302
  // src/colors.ts
126
303
  function resolveColor(color, palette) {
127
304
  if (color.startsWith("#")) return null;
@@ -1792,7 +1969,7 @@ function measureLegendText(text, fontSize) {
1792
1969
  }
1793
1970
  return w;
1794
1971
  }
1795
- var LEGEND_HEIGHT, LEGEND_PILL_PAD, LEGEND_PILL_FONT_SIZE, LEGEND_CAPSULE_PAD, LEGEND_DOT_R, LEGEND_ENTRY_FONT_SIZE, LEGEND_ENTRY_DOT_GAP, LEGEND_ENTRY_TRAIL, LEGEND_GROUP_GAP, LEGEND_EYE_SIZE, LEGEND_EYE_GAP, LEGEND_ICON_W, CHAR_W, DEFAULT_W, EYE_OPEN_PATH, EYE_CLOSED_PATH;
1972
+ var LEGEND_HEIGHT, LEGEND_PILL_PAD, LEGEND_PILL_FONT_SIZE, LEGEND_CAPSULE_PAD, LEGEND_DOT_R, LEGEND_ENTRY_FONT_SIZE, LEGEND_ENTRY_DOT_GAP, LEGEND_ENTRY_TRAIL, LEGEND_GROUP_GAP, LEGEND_EYE_SIZE, LEGEND_EYE_GAP, LEGEND_ICON_W, LEGEND_MAX_ENTRY_ROWS, CHAR_W, DEFAULT_W, EYE_OPEN_PATH, EYE_CLOSED_PATH;
1796
1973
  var init_legend_constants = __esm({
1797
1974
  "src/utils/legend-constants.ts"() {
1798
1975
  "use strict";
@@ -1808,6 +1985,7 @@ var init_legend_constants = __esm({
1808
1985
  LEGEND_EYE_SIZE = 14;
1809
1986
  LEGEND_EYE_GAP = 6;
1810
1987
  LEGEND_ICON_W = 20;
1988
+ LEGEND_MAX_ENTRY_ROWS = 3;
1811
1989
  CHAR_W = {
1812
1990
  " ": 0.28,
1813
1991
  "!": 0.28,
@@ -2217,12 +2395,11 @@ function getLegendReservedHeight(config, state, containerWidth) {
2217
2395
  const layout = computeLegendLayout(config, state, containerWidth);
2218
2396
  return layout.height;
2219
2397
  }
2220
- var LEGEND_MAX_ENTRY_ROWS, CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP;
2398
+ var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP;
2221
2399
  var init_legend_layout = __esm({
2222
2400
  "src/utils/legend-layout.ts"() {
2223
2401
  "use strict";
2224
2402
  init_legend_constants();
2225
- LEGEND_MAX_ENTRY_ROWS = 3;
2226
2403
  CONTROL_PILL_PAD = 16;
2227
2404
  CONTROL_FONT_SIZE = 11;
2228
2405
  CONTROL_ICON_GAP = 4;
@@ -3433,7 +3610,8 @@ function parseSequenceDgmo(content) {
3433
3610
  if (top.block.type === "if") {
3434
3611
  const branch = {
3435
3612
  label: elseIfMatch[1].trim(),
3436
- children: []
3613
+ children: [],
3614
+ lineNumber
3437
3615
  };
3438
3616
  if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
3439
3617
  top.block.elseIfBranches.push(branch);
@@ -3456,6 +3634,7 @@ function parseSequenceDgmo(content) {
3456
3634
  if (top.block.type === "if") {
3457
3635
  top.inElse = true;
3458
3636
  top.activeElseIfBranch = void 0;
3637
+ top.block.elseLineNumber = lineNumber;
3459
3638
  }
3460
3639
  }
3461
3640
  continue;
@@ -5849,7 +6028,8 @@ function buildExtendedChartOption(parsed, palette, isDark) {
5849
6028
  }
5850
6029
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark);
5851
6030
  if (parsed.type === "sankey") {
5852
- return buildSankeyOption(parsed, textColor, colors, titleConfig);
6031
+ const bg = isDark ? palette.surface : palette.bg;
6032
+ return buildSankeyOption(parsed, textColor, colors, bg, titleConfig);
5853
6033
  }
5854
6034
  if (parsed.type === "chord") {
5855
6035
  const bg = isDark ? palette.surface : palette.bg;
@@ -5892,7 +6072,7 @@ function buildExtendedChartOption(parsed, palette, isDark) {
5892
6072
  titleConfig
5893
6073
  );
5894
6074
  }
5895
- function buildSankeyOption(parsed, textColor, colors, titleConfig) {
6075
+ function buildSankeyOption(parsed, textColor, colors, bg, titleConfig) {
5896
6076
  const nodeSet = /* @__PURE__ */ new Set();
5897
6077
  if (parsed.links) {
5898
6078
  for (const link of parsed.links) {
@@ -5900,12 +6080,15 @@ function buildSankeyOption(parsed, textColor, colors, titleConfig) {
5900
6080
  nodeSet.add(link.target);
5901
6081
  }
5902
6082
  }
5903
- const nodes = Array.from(nodeSet).map((name, index) => ({
5904
- name,
5905
- itemStyle: {
5906
- color: parsed.nodeColors?.[name] ?? colors[index % colors.length]
5907
- }
5908
- }));
6083
+ const tintNode = (c) => mix(c, bg, 75);
6084
+ const tintLink = (c) => mix(c, bg, 45);
6085
+ const nodeColorMap = /* @__PURE__ */ new Map();
6086
+ const nodes = Array.from(nodeSet).map((name, index) => {
6087
+ const raw = parsed.nodeColors?.[name] ?? colors[index % colors.length];
6088
+ const tinted = tintNode(raw);
6089
+ nodeColorMap.set(name, tintLink(raw));
6090
+ return { name, itemStyle: { color: tinted } };
6091
+ });
5909
6092
  return {
5910
6093
  ...CHART_BASE,
5911
6094
  title: titleConfig,
@@ -5928,11 +6111,13 @@ function buildSankeyOption(parsed, textColor, colors, titleConfig) {
5928
6111
  source: link.source,
5929
6112
  target: link.target,
5930
6113
  value: link.value,
5931
- ...link.color && { lineStyle: { color: link.color } }
6114
+ lineStyle: {
6115
+ color: link.color ? tintLink(link.color) : nodeColorMap.get(link.source)
6116
+ }
5932
6117
  })),
5933
6118
  lineStyle: {
5934
- color: "gradient",
5935
- curveness: 0.5
6119
+ curveness: 0.5,
6120
+ opacity: 0.6
5936
6121
  },
5937
6122
  label: {
5938
6123
  color: textColor,
@@ -6189,16 +6374,6 @@ function getExtendedChartLegendGroups(parsed, colors) {
6189
6374
  }
6190
6375
  return [];
6191
6376
  }
6192
- function rectsOverlap(a, b) {
6193
- return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
6194
- }
6195
- function rectCircleOverlap(rect, circle) {
6196
- const nearestX = Math.max(rect.x, Math.min(circle.cx, rect.x + rect.w));
6197
- const nearestY = Math.max(rect.y, Math.min(circle.cy, rect.y + rect.h));
6198
- const dx = nearestX - circle.cx;
6199
- const dy = nearestY - circle.cy;
6200
- return dx * dx + dy * dy < circle.r * circle.r;
6201
- }
6202
6377
  function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize, bg) {
6203
6378
  const labelHeight = fontSize + 4;
6204
6379
  const stepSize = labelHeight + 2;
@@ -6575,12 +6750,17 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6575
6750
  maxValue = Math.max(maxValue, value);
6576
6751
  });
6577
6752
  });
6753
+ const CHAR_WIDTH7 = 7;
6754
+ const ESTIMATED_CHART_WIDTH = 900;
6755
+ const longestCol = Math.max(...columns.map((c) => c.length), 0);
6756
+ const slotWidth = columns.length > 0 ? ESTIMATED_CHART_WIDTH / columns.length : Infinity;
6757
+ const needsRotation = longestCol * CHAR_WIDTH7 > slotWidth * 0.85;
6578
6758
  return {
6579
6759
  ...CHART_BASE,
6580
6760
  title: titleConfig,
6581
6761
  grid: {
6582
6762
  left: "3%",
6583
- right: "10%",
6763
+ right: "3%",
6584
6764
  bottom: "3%",
6585
6765
  top: parsed.title ? "15%" : "5%",
6586
6766
  containLabel: true
@@ -6588,6 +6768,7 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6588
6768
  xAxis: {
6589
6769
  type: "category",
6590
6770
  data: columns,
6771
+ position: "top",
6591
6772
  splitArea: {
6592
6773
  show: true
6593
6774
  },
@@ -6596,12 +6777,19 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6596
6777
  },
6597
6778
  axisLabel: {
6598
6779
  color: textColor,
6599
- fontSize: 16
6780
+ fontSize: 12,
6781
+ interval: 0,
6782
+ ...needsRotation && {
6783
+ rotate: -45,
6784
+ width: 200,
6785
+ overflow: "none"
6786
+ }
6600
6787
  }
6601
6788
  },
6602
6789
  yAxis: {
6603
6790
  type: "category",
6604
6791
  data: rowLabels,
6792
+ inverse: true,
6605
6793
  splitArea: {
6606
6794
  show: true
6607
6795
  },
@@ -6610,16 +6798,14 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6610
6798
  },
6611
6799
  axisLabel: {
6612
6800
  color: textColor,
6613
- fontSize: 16
6801
+ fontSize: 12,
6802
+ interval: 0
6614
6803
  }
6615
6804
  },
6616
6805
  visualMap: {
6806
+ show: false,
6617
6807
  min: minValue,
6618
6808
  max: maxValue,
6619
- calculable: true,
6620
- orient: "vertical",
6621
- right: "2%",
6622
- top: "center",
6623
6809
  inRange: {
6624
6810
  color: [
6625
6811
  mix(palette.primary, bg, 30),
@@ -6627,9 +6813,6 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6627
6813
  mix(palette.colors.yellow, bg, 30),
6628
6814
  mix(palette.colors.orange, bg, 30)
6629
6815
  ]
6630
- },
6631
- textStyle: {
6632
- color: textColor
6633
6816
  }
6634
6817
  },
6635
6818
  series: [
@@ -6647,9 +6830,8 @@ function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, t
6647
6830
  fontWeight: "bold"
6648
6831
  },
6649
6832
  emphasis: {
6650
- ...EMPHASIS_SELF
6651
- },
6652
- blur: BLUR_DIM
6833
+ disabled: true
6834
+ }
6653
6835
  }
6654
6836
  ]
6655
6837
  };
@@ -7503,6 +7685,7 @@ var init_echarts = __esm({
7503
7685
  init_fonts();
7504
7686
  init_branding();
7505
7687
  init_legend_svg();
7688
+ init_label_layout();
7506
7689
  init_palettes();
7507
7690
  init_color_utils();
7508
7691
  init_chart();
@@ -14071,8 +14254,7 @@ function computeLayout(parsed, _palette) {
14071
14254
  currentX += cl.width + COLUMN_GAP;
14072
14255
  }
14073
14256
  const totalWidth = currentX - COLUMN_GAP + DIAGRAM_PADDING3;
14074
- const legendSpace = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT : 0;
14075
- const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3 + legendSpace;
14257
+ const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3;
14076
14258
  return { columns: columnLayouts, totalWidth, totalHeight };
14077
14259
  }
14078
14260
  function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exportDims, activeTagGroup) {
@@ -14085,14 +14267,16 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
14085
14267
  svg.append("text").attr("class", "chart-title").attr("data-line-number", parsed.titleLineNumber ?? 0).attr("x", DIAGRAM_PADDING3).attr("y", DIAGRAM_PADDING3 + TITLE_FONT_SIZE).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
14086
14268
  }
14087
14269
  if (parsed.tagGroups.length > 0) {
14088
- const legendY = height - LEGEND_HEIGHT;
14270
+ const titleTextWidth = parsed.title ? measureLegendText(parsed.title, TITLE_FONT_SIZE) + 16 : 0;
14271
+ const legendX = DIAGRAM_PADDING3 + titleTextWidth;
14272
+ const legendY = DIAGRAM_PADDING3 + (TITLE_FONT_SIZE - LEGEND_HEIGHT) / 2;
14089
14273
  const legendConfig = {
14090
14274
  groups: parsed.tagGroups,
14091
14275
  position: { placement: "top-center", titleRelation: "below-title" },
14092
14276
  mode: exportDims ? "inline" : "fixed"
14093
14277
  };
14094
14278
  const legendState = { activeGroup: activeTagGroup ?? null };
14095
- const legendG = svg.append("g").attr("class", "kanban-legend").attr("transform", `translate(${DIAGRAM_PADDING3},${legendY})`);
14279
+ const legendG = svg.append("g").attr("class", "kanban-legend").attr("transform", `translate(${legendX},${legendY})`);
14096
14280
  renderLegendD3(
14097
14281
  legendG,
14098
14282
  legendConfig,
@@ -14100,7 +14284,7 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
14100
14284
  palette,
14101
14285
  isDark,
14102
14286
  void 0,
14103
- width - DIAGRAM_PADDING3 * 2
14287
+ width - legendX - DIAGRAM_PADDING3
14104
14288
  );
14105
14289
  }
14106
14290
  const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
@@ -15642,7 +15826,7 @@ function fitTextToNode(label, nodeWidth, nodeHeight) {
15642
15826
  const maxTextWidth = nodeWidth - NODE_TEXT_PADDING * 2;
15643
15827
  const lineHeight = 1.3;
15644
15828
  for (let fontSize = NODE_FONT_SIZE; fontSize >= MIN_NODE_FONT_SIZE; fontSize--) {
15645
- const charWidth2 = fontSize * CHAR_WIDTH_RATIO;
15829
+ const charWidth2 = fontSize * CHAR_WIDTH_RATIO2;
15646
15830
  const maxCharsPerLine = Math.floor(maxTextWidth / charWidth2);
15647
15831
  const maxLines = Math.floor((nodeHeight - 8) / (fontSize * lineHeight));
15648
15832
  if (maxCharsPerLine < 2 || maxLines < 1) continue;
@@ -15694,7 +15878,7 @@ function fitTextToNode(label, nodeWidth, nodeHeight) {
15694
15878
  }
15695
15879
  if (hardLines.length <= maxLines) return { lines: hardLines, fontSize };
15696
15880
  }
15697
- const charWidth = MIN_NODE_FONT_SIZE * CHAR_WIDTH_RATIO;
15881
+ const charWidth = MIN_NODE_FONT_SIZE * CHAR_WIDTH_RATIO2;
15698
15882
  const maxChars = Math.floor((nodeWidth - NODE_TEXT_PADDING * 2) / charWidth);
15699
15883
  const truncated = label.length > maxChars ? label.slice(0, maxChars - 1) + "\u2026" : label;
15700
15884
  return { lines: [truncated], fontSize: MIN_NODE_FONT_SIZE };
@@ -15853,7 +16037,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
15853
16037
  path.attr("marker-start", `url(#${revId})`);
15854
16038
  }
15855
16039
  if (le.label && le.labelX != null && le.labelY != null) {
15856
- const lw = le.label.length * EDGE_LABEL_FONT_SIZE4 * CHAR_WIDTH_RATIO;
16040
+ const lw = le.label.length * EDGE_LABEL_FONT_SIZE4 * CHAR_WIDTH_RATIO2;
15857
16041
  labelPositions.push({
15858
16042
  x: le.labelX,
15859
16043
  y: le.labelY + le.yOffset,
@@ -15911,7 +16095,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
15911
16095
  const descY = labelY + lineH / 2 + gap + META_FONT_SIZE3 / 2;
15912
16096
  nodeG.append("text").attr("x", 0).attr("y", labelY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", NODE_FONT_SIZE).attr("font-weight", "600").attr("fill", colors.text).text(node.label);
15913
16097
  const maxChars = Math.floor(
15914
- (ln.width - NODE_TEXT_PADDING * 2) / (META_FONT_SIZE3 * CHAR_WIDTH_RATIO)
16098
+ (ln.width - NODE_TEXT_PADDING * 2) / (META_FONT_SIZE3 * CHAR_WIDTH_RATIO2)
15915
16099
  );
15916
16100
  const desc = node.description.length > maxChars ? node.description.slice(0, maxChars - 1) + "\u2026" : node.description;
15917
16101
  const descEl = nodeG.append("text").attr("x", 0).attr("y", descY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", META_FONT_SIZE3).attr("fill", palette.textMuted).text(desc);
@@ -15952,7 +16136,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
15952
16136
  exportDims: options?.exportDims
15953
16137
  });
15954
16138
  }
15955
- var DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, META_FONT_SIZE3, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, CHAR_WIDTH_RATIO, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, lineGeneratorLR, lineGeneratorTB, lineGeneratorLinear2;
16139
+ var DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, META_FONT_SIZE3, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, CHAR_WIDTH_RATIO2, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, lineGeneratorLR, lineGeneratorTB, lineGeneratorLinear2;
15956
16140
  var init_renderer6 = __esm({
15957
16141
  "src/boxes-and-lines/renderer.ts"() {
15958
16142
  "use strict";
@@ -15973,7 +16157,7 @@ var init_renderer6 = __esm({
15973
16157
  COLLAPSE_BAR_HEIGHT3 = 4;
15974
16158
  ARROWHEAD_W2 = 5;
15975
16159
  ARROWHEAD_H2 = 4;
15976
- CHAR_WIDTH_RATIO = 0.6;
16160
+ CHAR_WIDTH_RATIO2 = 0.6;
15977
16161
  NODE_TEXT_PADDING = 12;
15978
16162
  GROUP_RX = 8;
15979
16163
  GROUP_LABEL_FONT_SIZE = 14;
@@ -16045,7 +16229,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
16045
16229
  }
16046
16230
  const nodeGeometry = /* @__PURE__ */ new Map();
16047
16231
  for (const name of g.nodes()) {
16048
- const pos = g.node(name);
16232
+ const pos = gNode(g, name);
16049
16233
  if (pos)
16050
16234
  nodeGeometry.set(name, {
16051
16235
  y: pos.y,
@@ -16055,14 +16239,14 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
16055
16239
  }
16056
16240
  const rankMap = /* @__PURE__ */ new Map();
16057
16241
  for (const name of g.nodes()) {
16058
- const pos = g.node(name);
16242
+ const pos = gNode(g, name);
16059
16243
  if (!pos) continue;
16060
16244
  const rankY = Math.round(pos.y);
16061
16245
  if (!rankMap.has(rankY)) rankMap.set(rankY, []);
16062
16246
  rankMap.get(rankY).push(name);
16063
16247
  }
16064
16248
  for (const [, rankNodes] of rankMap) {
16065
- rankNodes.sort((a, b) => g.node(a).x - g.node(b).x);
16249
+ rankNodes.sort((a, b) => gNode(g, a).x - gNode(g, b).x);
16066
16250
  }
16067
16251
  let anyMoved = false;
16068
16252
  for (const [, rankNodes] of rankMap) {
@@ -16089,10 +16273,10 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
16089
16273
  }
16090
16274
  for (const partition of partitions) {
16091
16275
  if (partition.length < 2) continue;
16092
- const xSlots = partition.map((name) => g.node(name).x).sort((a, b) => a - b);
16276
+ const xSlots = partition.map((name) => gNode(g, name).x).sort((a, b) => a - b);
16093
16277
  const basePositions = /* @__PURE__ */ new Map();
16094
16278
  for (const name of g.nodes()) {
16095
- const pos = g.node(name);
16279
+ const pos = gNode(g, name);
16096
16280
  if (pos) basePositions.set(name, pos.x);
16097
16281
  }
16098
16282
  const currentPenalty = computeEdgePenalty(
@@ -16170,7 +16354,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
16170
16354
  }
16171
16355
  if (bestPerm.some((name, i) => name !== partition[i])) {
16172
16356
  for (let i = 0; i < bestPerm.length; i++) {
16173
- g.node(bestPerm[i]).x = xSlots[i];
16357
+ gNode(g, bestPerm[i]).x = xSlots[i];
16174
16358
  const rankIdx = rankNodes.indexOf(partition[i]);
16175
16359
  if (rankIdx >= 0) rankNodes[rankIdx] = bestPerm[i];
16176
16360
  }
@@ -16180,10 +16364,10 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
16180
16364
  }
16181
16365
  if (anyMoved) {
16182
16366
  for (const edge of edgeList) {
16183
- const edgeData = g.edge(edge.source, edge.target);
16367
+ const edgeData = gEdge(g, edge.source, edge.target);
16184
16368
  if (!edgeData) continue;
16185
- const srcPos = g.node(edge.source);
16186
- const tgtPos = g.node(edge.target);
16369
+ const srcPos = gNode(g, edge.source);
16370
+ const tgtPos = gNode(g, edge.target);
16187
16371
  if (!srcPos || !tgtPos) continue;
16188
16372
  const srcBottom = { x: srcPos.x, y: srcPos.y + srcPos.height / 2 };
16189
16373
  const tgtTop = { x: tgtPos.x, y: tgtPos.y - tgtPos.height / 2 };
@@ -17627,11 +17811,13 @@ function layoutC4Deployment(parsed, activeTagGroup) {
17627
17811
  height: totalHeight
17628
17812
  };
17629
17813
  }
17630
- var CHAR_WIDTH5, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, DESC_LINE_HEIGHT, DESC_CHAR_WIDTH, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_CHAR_WIDTH, MARGIN4, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, LEGEND_HEIGHT4, LEGEND_PILL_PAD4, LEGEND_DOT_R4, LEGEND_ENTRY_DOT_GAP4, LEGEND_ENTRY_TRAIL4, LEGEND_CAPSULE_PAD4, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
17814
+ var gNode, gEdge, CHAR_WIDTH5, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, DESC_LINE_HEIGHT, DESC_CHAR_WIDTH, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_CHAR_WIDTH, MARGIN4, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, LEGEND_HEIGHT4, LEGEND_PILL_PAD4, LEGEND_DOT_R4, LEGEND_ENTRY_DOT_GAP4, LEGEND_ENTRY_TRAIL4, LEGEND_CAPSULE_PAD4, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
17631
17815
  var init_layout6 = __esm({
17632
17816
  "src/c4/layout.ts"() {
17633
17817
  "use strict";
17634
17818
  init_legend_constants();
17819
+ gNode = (g, name) => g.node(name);
17820
+ gEdge = (g, v, w) => g.edge(v, w);
17635
17821
  CHAR_WIDTH5 = 8;
17636
17822
  MIN_NODE_WIDTH = 160;
17637
17823
  MAX_NODE_WIDTH = 260;
@@ -25435,7 +25621,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25435
25621
  for (const branch of el.elseIfBranches) {
25436
25622
  elseIfBranchData.push({
25437
25623
  label: branch.label,
25438
- indices: collectMsgIndices(branch.children)
25624
+ indices: collectMsgIndices(branch.children),
25625
+ lineNumber: branch.lineNumber
25439
25626
  });
25440
25627
  }
25441
25628
  }
@@ -25496,14 +25683,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25496
25683
  x1: frameX,
25497
25684
  y1: dividerY,
25498
25685
  x2: frameX + frameW,
25499
- y2: dividerY
25686
+ y2: dividerY,
25687
+ blockLine: branchData.lineNumber
25500
25688
  });
25501
25689
  deferredLabels.push({
25502
25690
  x: frameX + 6,
25503
25691
  y: dividerY + 14,
25504
25692
  text: `else if ${branchData.label}`,
25505
25693
  bold: false,
25506
- italic: true
25694
+ italic: true,
25695
+ blockLine: branchData.lineNumber
25507
25696
  });
25508
25697
  }
25509
25698
  }
@@ -25521,14 +25710,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25521
25710
  x1: frameX,
25522
25711
  y1: dividerY,
25523
25712
  x2: frameX + frameW,
25524
- y2: dividerY
25713
+ y2: dividerY,
25714
+ blockLine: el.elseLineNumber
25525
25715
  });
25526
25716
  deferredLabels.push({
25527
25717
  x: frameX + 6,
25528
25718
  y: dividerY + 14,
25529
25719
  text: "else",
25530
25720
  bold: false,
25531
- italic: true
25721
+ italic: true,
25722
+ blockLine: el.elseLineNumber
25532
25723
  });
25533
25724
  }
25534
25725
  }
@@ -25574,7 +25765,9 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25574
25765
  }
25575
25766
  });
25576
25767
  for (const ln of deferredLines) {
25577
- svg.append("line").attr("x1", ln.x1).attr("y1", ln.y1).attr("x2", ln.x2).attr("y2", ln.y2).attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", "2 3");
25768
+ const line10 = svg.append("line").attr("x1", ln.x1).attr("y1", ln.y1).attr("x2", ln.x2).attr("y2", ln.y2).attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", "2 3").attr("class", "block-divider");
25769
+ if (ln.blockLine !== void 0)
25770
+ line10.attr("data-block-line", String(ln.blockLine));
25578
25771
  }
25579
25772
  for (const lbl of deferredLabels) {
25580
25773
  const t = svg.append("text").attr("x", lbl.x).attr("y", lbl.y).attr("fill", palette.text).attr("font-size", 11).attr("class", "block-label").text(lbl.text);
@@ -28398,7 +28591,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28398
28591
  }
28399
28592
  evG.append("rect").attr("x", x).attr("y", y - BAR_H2 / 2).attr("width", rectW).attr("height", BAR_H2).attr("rx", 4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", 2);
28400
28593
  if (labelFitsInside) {
28401
- evG.append("text").attr("x", x + 8).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "start").attr("fill", textColor).attr("font-size", "14px").attr("font-weight", "700").text(ev.label);
28594
+ evG.append("text").attr("x", x + 8).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "start").attr("fill", textColor).attr("font-size", "13px").text(ev.label);
28402
28595
  } else {
28403
28596
  const wouldFlipLeft = x + rectW > innerWidth * 0.6;
28404
28597
  const labelFitsLeft = x - 6 - estLabelWidth > 0;
@@ -28552,7 +28745,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28552
28745
  }
28553
28746
  evG.append("rect").attr("x", x).attr("y", y - BAR_H2 / 2).attr("width", rectW).attr("height", BAR_H2).attr("rx", 4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", 2);
28554
28747
  if (labelFitsInside) {
28555
- evG.append("text").attr("x", x + 8).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "start").attr("fill", textColor).attr("font-size", "14px").attr("font-weight", "700").text(ev.label);
28748
+ evG.append("text").attr("x", x + 8).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "start").attr("fill", textColor).attr("font-size", "13px").text(ev.label);
28556
28749
  } else {
28557
28750
  const wouldFlipLeft = x + rectW > innerWidth * 0.6;
28558
28751
  const labelFitsLeft = x - 6 - estLabelWidth > 0;
@@ -28920,7 +29113,7 @@ function regionCentroid(circles, inside) {
28920
29113
  }
28921
29114
  function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
28922
29115
  const { vennSets, vennOverlaps, title } = parsed;
28923
- if (vennSets.length < 2) return;
29116
+ if (vennSets.length < 2 || vennSets.length > 3) return;
28924
29117
  const init2 = initD3Chart(container, palette, exportDims);
28925
29118
  if (!init2) return;
28926
29119
  const { svg, width, height, textColor, colors } = init2;
@@ -28976,7 +29169,9 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
28976
29169
  marginBottom
28977
29170
  ).map((c) => ({ ...c, y: c.y + titleHeight }));
28978
29171
  const scaledR = circles[0].r;
28979
- svg.append("style").text("circle:focus, circle:focus-visible { outline: none !important; }");
29172
+ svg.append("style").text(
29173
+ "circle:focus, circle:focus-visible { outline-solid: none !important; }"
29174
+ );
28980
29175
  renderChartTitle(
28981
29176
  svg,
28982
29177
  title,
@@ -29158,7 +29353,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
29158
29353
  }
29159
29354
  const hoverGroup = svg.append("g");
29160
29355
  circles.forEach((c, i) => {
29161
- hoverGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", String(vennSets[i].lineNumber)).style("cursor", onClickItem ? "pointer" : "default").style("outline", "none").on("mouseenter", () => {
29356
+ hoverGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", String(vennSets[i].lineNumber)).style("cursor", onClickItem ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
29162
29357
  showRegionOverlay([i]);
29163
29358
  }).on("mouseleave", () => {
29164
29359
  hideAllOverlays();
@@ -29196,7 +29391,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
29196
29391
  const declaredOv = vennOverlaps.find(
29197
29392
  (ov) => ov.sets.length === sets.length && ov.sets.every((s, k) => s === sets[k])
29198
29393
  );
29199
- hoverGroup.append("circle").attr("cx", centroid.x).attr("cy", centroid.y).attr("r", overlayR).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", declaredOv ? String(declaredOv.lineNumber) : "").style("cursor", onClickItem && declaredOv ? "pointer" : "default").style("outline", "none").on("mouseenter", () => {
29394
+ hoverGroup.append("circle").attr("cx", centroid.x).attr("cy", centroid.y).attr("r", overlayR).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", declaredOv ? String(declaredOv.lineNumber) : "").style("cursor", onClickItem && declaredOv ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
29200
29395
  showRegionOverlay(idxs);
29201
29396
  }).on("mouseleave", () => {
29202
29397
  hideAllOverlays();
@@ -29330,8 +29525,8 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
29330
29525
  const LABEL_MAX_FONT = 48;
29331
29526
  const LABEL_MIN_FONT = 14;
29332
29527
  const LABEL_PAD2 = 40;
29333
- const CHAR_WIDTH_RATIO2 = 0.6;
29334
- const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO2;
29528
+ const CHAR_WIDTH_RATIO3 = 0.6;
29529
+ const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO3;
29335
29530
  const quadrantLabelLayout = (text, qw2, qh2) => {
29336
29531
  const availW = qw2 - LABEL_PAD2;
29337
29532
  const availH = qh2 - LABEL_PAD2;
@@ -29475,16 +29670,45 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
29475
29670
  if (x < 0.5 && y < 0.5) return "bottom-left";
29476
29671
  return "bottom-right";
29477
29672
  };
29673
+ const POINT_RADIUS = 6;
29674
+ const POINT_LABEL_FONT_SIZE = 12;
29675
+ const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
29676
+ const layout = labelLayouts.get(d.label.text);
29677
+ const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO3;
29678
+ const totalH = layout.lines.length * layout.fontSize * 1.2;
29679
+ return {
29680
+ x: d.labelX - totalW / 2,
29681
+ y: d.labelY - totalH / 2,
29682
+ w: totalW,
29683
+ h: totalH
29684
+ };
29685
+ });
29686
+ const pointPixels = quadrantPoints.map((point) => ({
29687
+ label: point.label,
29688
+ cx: xScale(point.x),
29689
+ cy: yScale(point.y)
29690
+ }));
29691
+ const placedPointLabels = computeQuadrantPointLabels(
29692
+ pointPixels,
29693
+ { left: 0, top: 0, right: chartWidth, bottom: chartHeight },
29694
+ quadrantLabelObstacles,
29695
+ POINT_RADIUS,
29696
+ POINT_LABEL_FONT_SIZE
29697
+ );
29478
29698
  const pointsG = chartG.append("g").attr("class", "points");
29479
- quadrantPoints.forEach((point) => {
29699
+ quadrantPoints.forEach((point, i) => {
29480
29700
  const cx = xScale(point.x);
29481
29701
  const cy = yScale(point.y);
29482
29702
  const quadrant = getPointQuadrant(point.x, point.y);
29483
29703
  const quadDef = quadrantDefs.find((d) => d.position === quadrant);
29484
29704
  const pointColor = quadDef?.label?.color ?? defaultColors[quadDef?.colorIdx ?? 0];
29705
+ const placed = placedPointLabels[i];
29485
29706
  const pointG = pointsG.append("g").attr("class", "point-group").attr("data-line-number", String(point.lineNumber));
29486
- pointG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 6).attr("fill", "#ffffff").attr("stroke", pointColor).attr("stroke-width", 2);
29487
- pointG.append("text").attr("x", cx).attr("y", cy - 10).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "12px").attr("font-weight", "700").style("text-shadow", `0 1px 2px ${shadowColor}`).text(point.label);
29707
+ if (placed.connectorLine) {
29708
+ pointG.append("line").attr("x1", placed.connectorLine.x1).attr("y1", placed.connectorLine.y1).attr("x2", placed.connectorLine.x2).attr("y2", placed.connectorLine.y2).attr("stroke", pointColor).attr("stroke-width", 1).attr("opacity", 0.5);
29709
+ }
29710
+ pointG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", POINT_RADIUS).attr("fill", "#ffffff").attr("stroke", pointColor).attr("stroke-width", 2);
29711
+ pointG.append("text").attr("x", placed.x).attr("y", placed.y).attr("text-anchor", placed.anchor).attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", `${POINT_LABEL_FONT_SIZE}px`).attr("font-weight", "700").style("text-shadow", `0 1px 2px ${shadowColor}`).text(point.label);
29488
29712
  const tipHtml = `<strong>${point.label}</strong><br>x: ${point.x.toFixed(2)}, y: ${point.y.toFixed(2)}`;
29489
29713
  pointG.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", (event) => {
29490
29714
  showTooltip(tooltip, tipHtml, event);
@@ -29493,7 +29717,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
29493
29717
  showTooltip(tooltip, tipHtml, event);
29494
29718
  }).on("mouseleave", () => {
29495
29719
  hideTooltip(tooltip);
29496
- pointG.select("circle").attr("r", 6);
29720
+ pointG.select("circle").attr("r", POINT_RADIUS);
29497
29721
  }).on("click", () => {
29498
29722
  if (onClickItem && point.lineNumber) onClickItem(point.lineNumber);
29499
29723
  });
@@ -29960,6 +30184,7 @@ var init_d3 = __esm({
29960
30184
  "use strict";
29961
30185
  init_fonts();
29962
30186
  init_branding();
30187
+ init_label_layout();
29963
30188
  init_colors();
29964
30189
  init_palettes();
29965
30190
  init_color_utils();
@@ -31736,6 +31961,7 @@ export {
31736
31961
  computeTimeTicks,
31737
31962
  contrastText,
31738
31963
  decodeDiagramUrl,
31964
+ draculaPalette,
31739
31965
  encodeDiagramUrl,
31740
31966
  extractDiagramSymbols,
31741
31967
  extractTagDeclarations,
@@ -31779,6 +32005,7 @@ export {
31779
32005
  looksLikeSitemap,
31780
32006
  looksLikeState,
31781
32007
  makeDgmoError,
32008
+ monokaiPalette,
31782
32009
  mute,
31783
32010
  nord,
31784
32011
  nordPalette,