@diagrammo/dgmo 0.48.1 → 0.48.2

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/auto.mjs CHANGED
@@ -61550,20 +61550,35 @@ function renderRing(container, parsed, palette, isDark, onClickItem, exportDims)
61550
61550
  }
61551
61551
  if (inBandLabelsVisible) {
61552
61552
  const labelsG = svg.append("g").attr("class", "ring-labels");
61553
+ const labelDefs = labelsG.append("defs");
61554
+ let ringLabelSeq = 0;
61553
61555
  for (let i = 0; i < N; i++) {
61554
61556
  const layer = parsed.layers[i];
61555
61557
  const fill2 = layerFills[i];
61556
61558
  const isInnermost = i === 0;
61557
- const labelY = isInnermost ? cy : cy - (i + 0.5) * thickness;
61558
- const fittedFont = labelFont;
61559
61559
  const textColor = contrastText(
61560
61560
  fill2,
61561
61561
  palette.textOnFillLight,
61562
61562
  palette.textOnFillDark
61563
61563
  );
61564
61564
  const labelG = labelsG.append("g").attr("class", "ring-label-group").attr("data-line-number", layer.lineNumber);
61565
- const label = labelG.append("text").attr("class", "ring-label").attr("x", cx).attr("y", labelY).attr("dy", "0.35em").attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", fittedFont).attr("font-weight", 600).style("pointer-events", "none");
61566
- renderInlineText(label, layer.label, palette, fittedFont);
61565
+ if (isInnermost) {
61566
+ const label = labelG.append("text").attr("class", "ring-label").attr("x", cx).attr("y", cy).attr("dy", "0.35em").attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", labelFont).attr("font-weight", 600).style("pointer-events", "none");
61567
+ renderInlineText(label, layer.label, palette, labelFont);
61568
+ continue;
61569
+ }
61570
+ drawRingCurvedLabel(
61571
+ labelG,
61572
+ labelDefs,
61573
+ `dgmo-ring-lbl-${ringLabelSeq++}`,
61574
+ layer.label,
61575
+ cx,
61576
+ cy,
61577
+ (i + 0.5) * thickness,
61578
+ labelFont,
61579
+ textColor,
61580
+ palette
61581
+ );
61567
61582
  }
61568
61583
  }
61569
61584
  if (hasAnyDescription || !inBandLabelsVisible) {
@@ -61583,6 +61598,34 @@ function renderRing(container, parsed, palette, isDark, onClickItem, exportDims)
61583
61598
  });
61584
61599
  }
61585
61600
  }
61601
+ function drawRingCurvedLabel(labelG, defs, pathId, text, cx, cy, rMid, labelFont, textColor, palette) {
61602
+ const PAD = 1.18;
61603
+ let fs = labelFont;
61604
+ let rPath = rMid - fs * 0.32;
61605
+ const maxLen = MAX_LABEL_ARC * rPath;
61606
+ const wPadded = measureText(text, labelFont) * PAD;
61607
+ if (wPadded > maxLen) {
61608
+ fs = Math.max(LABEL_FONT_MIN2, Math.floor(labelFont * maxLen / wPadded));
61609
+ rPath = rMid - fs * 0.32;
61610
+ }
61611
+ const arcLen = measureText(text, fs) * PAD;
61612
+ const halfAngle = Math.min(arcLen / (2 * rPath), MAX_LABEL_ARC / 2);
61613
+ const largeArc = halfAngle * 2 > Math.PI ? 1 : 0;
61614
+ const p0x = cx - Math.sin(halfAngle) * rPath;
61615
+ const p0y = cy - Math.cos(halfAngle) * rPath;
61616
+ const p1x = cx + Math.sin(halfAngle) * rPath;
61617
+ const p1y = cy - Math.cos(halfAngle) * rPath;
61618
+ const d = `M ${p0x.toFixed(2)} ${p0y.toFixed(2)} A ${rPath.toFixed(2)} ${rPath.toFixed(2)} 0 ${largeArc} 1 ${p1x.toFixed(2)} ${p1y.toFixed(2)}`;
61619
+ defs.append("path").attr("id", pathId).attr("d", d);
61620
+ const label = labelG.append("text").attr("class", "ring-label").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", fs).attr("font-weight", 600).style("pointer-events", "none");
61621
+ const textPath = label.append("textPath").attr("href", `#${pathId}`).attr("startOffset", "50%").attr("text-anchor", "middle");
61622
+ renderInlineText(
61623
+ textPath,
61624
+ text,
61625
+ palette,
61626
+ fs
61627
+ );
61628
+ }
61586
61629
  function renderRingForExport(container, parsed, palette, isDark, exportDims) {
61587
61630
  renderRing(container, parsed, palette, isDark, void 0, exportDims);
61588
61631
  }
@@ -61666,7 +61709,7 @@ function truncateWithEllipsis2(lines, cap) {
61666
61709
  function clamp2(x, lo, hi) {
61667
61710
  return Math.max(lo, Math.min(hi, x));
61668
61711
  }
61669
- var TITLE_AREA_HEIGHT3, H_MARGIN_FRAC2, V_MARGIN2, RING_AREA_FRAC_NO_DESC, RING_AREA_FRAC_WITH_DESC, DESC_GAP2, DESC_ACCENT_WIDTH2, DESC_ACCENT_GAP2, BULLET_BODY_INDENT2, RING_STROKE_WIDTH, RING_STROKE_OPACITY, DESC_LINE_CAP, LABEL_FONT_MIN2, LABEL_FONT_MAX2, DESC_FONT_MIN2, DESC_FONT_MAX2;
61712
+ var TITLE_AREA_HEIGHT3, H_MARGIN_FRAC2, V_MARGIN2, RING_AREA_FRAC_NO_DESC, RING_AREA_FRAC_WITH_DESC, DESC_GAP2, DESC_ACCENT_WIDTH2, DESC_ACCENT_GAP2, BULLET_BODY_INDENT2, RING_STROKE_WIDTH, RING_STROKE_OPACITY, DESC_LINE_CAP, LABEL_FONT_MIN2, LABEL_FONT_MAX2, DESC_FONT_MIN2, DESC_FONT_MAX2, MAX_LABEL_ARC;
61670
61713
  var init_renderer22 = __esm({
61671
61714
  "src/ring/renderer.ts"() {
61672
61715
  "use strict";
@@ -61694,6 +61737,7 @@ var init_renderer22 = __esm({
61694
61737
  LABEL_FONT_MAX2 = 22;
61695
61738
  DESC_FONT_MIN2 = 11;
61696
61739
  DESC_FONT_MAX2 = 15;
61740
+ MAX_LABEL_ARC = 1.6 * Math.PI;
61697
61741
  }
61698
61742
  });
61699
61743
 
@@ -69812,15 +69856,21 @@ var BLOCK_CSS = `
69812
69856
  opacity: 1;
69813
69857
  }
69814
69858
 
69815
- /* === Open state: frame the whole block ===
69859
+ /* === Frame the whole block ===
69816
69860
  * When the source panel is open, diagram + toolbar + code get one shared
69817
- * border so the code visibly belongs to the chart above it, not to the
69818
- * surrounding prose. Closed blocks stay frameless. */
69819
- .dgmo:has(> .dgmo-source-wrap[open]) {
69820
- border: 1px solid rgba(127, 127, 127, 0.3);
69861
+ * border so the code visibly belongs to the chart above it. The frame box
69862
+ * (border + padding) is RESERVED at all times \u2014 with the border transparent
69863
+ * while closed \u2014 so opening the panel only paints the border in. It never
69864
+ * reflows the diagram or shifts the toolbar: the SVG box and toolbar position
69865
+ * are identical open vs closed. */
69866
+ .dgmo:has(> .dgmo-source-wrap) {
69867
+ border: 1px solid transparent;
69821
69868
  border-radius: 0.6em;
69822
69869
  padding: 0.75em 0.75em 0.5em;
69823
69870
  }
69871
+ .dgmo:has(> .dgmo-source-wrap[open]) {
69872
+ border-color: rgba(127, 127, 127, 0.3);
69873
+ }
69824
69874
 
69825
69875
  /* === Source panel (hidden until toggled) === */
69826
69876
  .dgmo-source-inner {
@@ -70101,7 +70151,7 @@ var CSS = AUTO_BASE_CSS + "\n" + BLOCK_CSS + "\n" + darkScopedBlockCss(BLOCK_CSS
70101
70151
 
70102
70152
  // src/auto/shared.ts
70103
70153
  init_safe_href();
70104
- var VERSION = "0.48.1";
70154
+ var VERSION = "0.48.2";
70105
70155
  var ARIA_LABEL_MAX = 200;
70106
70156
  var COPIED_INTERACTION_MS = 1500;
70107
70157
  var LOG_PREFIX = "[dgmo]";
package/dist/block.css CHANGED
@@ -109,15 +109,21 @@
109
109
  opacity: 1;
110
110
  }
111
111
 
112
- /* === Open state: frame the whole block ===
112
+ /* === Frame the whole block ===
113
113
  * When the source panel is open, diagram + toolbar + code get one shared
114
- * border so the code visibly belongs to the chart above it, not to the
115
- * surrounding prose. Closed blocks stay frameless. */
116
- .dgmo:has(> .dgmo-source-wrap[open]) {
117
- border: 1px solid rgba(127, 127, 127, 0.3);
114
+ * border so the code visibly belongs to the chart above it. The frame box
115
+ * (border + padding) is RESERVED at all times — with the border transparent
116
+ * while closed — so opening the panel only paints the border in. It never
117
+ * reflows the diagram or shifts the toolbar: the SVG box and toolbar position
118
+ * are identical open vs closed. */
119
+ .dgmo:has(> .dgmo-source-wrap) {
120
+ border: 1px solid transparent;
118
121
  border-radius: 0.6em;
119
122
  padding: 0.75em 0.75em 0.5em;
120
123
  }
124
+ .dgmo:has(> .dgmo-source-wrap[open]) {
125
+ border-color: rgba(127, 127, 127, 0.3);
126
+ }
121
127
 
122
128
  /* === Source panel (hidden until toggled) === */
123
129
  .dgmo-source-inner {
package/dist/block.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  encodeDiagramUrl,
6
6
  render
7
- } from "./chunk-UC7TFHUM.js";
7
+ } from "./chunk-PZMETGLY.js";
8
8
  import "./chunk-PBAHEPLQ.js";
9
9
  import "./chunk-OJR3MQYV.js";
10
10
  import "./chunk-UWN33VNX.js";
@@ -583,15 +583,21 @@ var BLOCK_CSS = `
583
583
  opacity: 1;
584
584
  }
585
585
 
586
- /* === Open state: frame the whole block ===
586
+ /* === Frame the whole block ===
587
587
  * When the source panel is open, diagram + toolbar + code get one shared
588
- * border so the code visibly belongs to the chart above it, not to the
589
- * surrounding prose. Closed blocks stay frameless. */
590
- .dgmo:has(> .dgmo-source-wrap[open]) {
591
- border: 1px solid rgba(127, 127, 127, 0.3);
588
+ * border so the code visibly belongs to the chart above it. The frame box
589
+ * (border + padding) is RESERVED at all times \u2014 with the border transparent
590
+ * while closed \u2014 so opening the panel only paints the border in. It never
591
+ * reflows the diagram or shifts the toolbar: the SVG box and toolbar position
592
+ * are identical open vs closed. */
593
+ .dgmo:has(> .dgmo-source-wrap) {
594
+ border: 1px solid transparent;
592
595
  border-radius: 0.6em;
593
596
  padding: 0.75em 0.75em 0.5em;
594
597
  }
598
+ .dgmo:has(> .dgmo-source-wrap[open]) {
599
+ border-color: rgba(127, 127, 127, 0.3);
600
+ }
595
601
 
596
602
  /* === Source panel (hidden until toggled) === */
597
603
  .dgmo-source-inner {
@@ -3046,7 +3046,7 @@ async function exportPyramid(ctx) {
3046
3046
  async function exportRing(ctx) {
3047
3047
  const { content, theme, palette } = ctx;
3048
3048
  const { parseRing } = await import("./parser-O5J4VBII.js");
3049
- const { renderRingForExport } = await import("./renderer-ZTR3DER3.js");
3049
+ const { renderRingForExport } = await import("./renderer-UV6MI64M.js");
3050
3050
  const effectivePalette = await resolveExportPalette(theme, palette);
3051
3051
  const ringParsed = parseRing(content);
3052
3052
  if (ringParsed.error || ringParsed.layers.length === 0) return "";
@@ -3965,4 +3965,4 @@ export {
3965
3965
  encodeDiagramUrl,
3966
3966
  decodeDiagramUrl
3967
3967
  };
3968
- //# sourceMappingURL=chunk-UC7TFHUM.js.map
3968
+ //# sourceMappingURL=chunk-PZMETGLY.js.map
@@ -16,7 +16,8 @@ import {
16
16
  FONT_FAMILY
17
17
  } from "./chunk-6KFWC4CO.js";
18
18
  import {
19
- CHAR_WIDTH_RATIO
19
+ CHAR_WIDTH_RATIO,
20
+ measureText
20
21
  } from "./chunk-WCT3NYCV.js";
21
22
  import {
22
23
  contrastText,
@@ -127,20 +128,35 @@ function renderRing(container, parsed, palette, isDark, onClickItem, exportDims)
127
128
  }
128
129
  if (inBandLabelsVisible) {
129
130
  const labelsG = svg.append("g").attr("class", "ring-labels");
131
+ const labelDefs = labelsG.append("defs");
132
+ let ringLabelSeq = 0;
130
133
  for (let i = 0; i < N; i++) {
131
134
  const layer = parsed.layers[i];
132
135
  const fill = layerFills[i];
133
136
  const isInnermost = i === 0;
134
- const labelY = isInnermost ? cy : cy - (i + 0.5) * thickness;
135
- const fittedFont = labelFont;
136
137
  const textColor = contrastText(
137
138
  fill,
138
139
  palette.textOnFillLight,
139
140
  palette.textOnFillDark
140
141
  );
141
142
  const labelG = labelsG.append("g").attr("class", "ring-label-group").attr("data-line-number", layer.lineNumber);
142
- const label = labelG.append("text").attr("class", "ring-label").attr("x", cx).attr("y", labelY).attr("dy", "0.35em").attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", fittedFont).attr("font-weight", 600).style("pointer-events", "none");
143
- renderInlineText(label, layer.label, palette, fittedFont);
143
+ if (isInnermost) {
144
+ const label = labelG.append("text").attr("class", "ring-label").attr("x", cx).attr("y", cy).attr("dy", "0.35em").attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", labelFont).attr("font-weight", 600).style("pointer-events", "none");
145
+ renderInlineText(label, layer.label, palette, labelFont);
146
+ continue;
147
+ }
148
+ drawRingCurvedLabel(
149
+ labelG,
150
+ labelDefs,
151
+ `dgmo-ring-lbl-${ringLabelSeq++}`,
152
+ layer.label,
153
+ cx,
154
+ cy,
155
+ (i + 0.5) * thickness,
156
+ labelFont,
157
+ textColor,
158
+ palette
159
+ );
144
160
  }
145
161
  }
146
162
  if (hasAnyDescription || !inBandLabelsVisible) {
@@ -160,6 +176,35 @@ function renderRing(container, parsed, palette, isDark, onClickItem, exportDims)
160
176
  });
161
177
  }
162
178
  }
179
+ var MAX_LABEL_ARC = 1.6 * Math.PI;
180
+ function drawRingCurvedLabel(labelG, defs, pathId, text, cx, cy, rMid, labelFont, textColor, palette) {
181
+ const PAD = 1.18;
182
+ let fs = labelFont;
183
+ let rPath = rMid - fs * 0.32;
184
+ const maxLen = MAX_LABEL_ARC * rPath;
185
+ const wPadded = measureText(text, labelFont) * PAD;
186
+ if (wPadded > maxLen) {
187
+ fs = Math.max(LABEL_FONT_MIN, Math.floor(labelFont * maxLen / wPadded));
188
+ rPath = rMid - fs * 0.32;
189
+ }
190
+ const arcLen = measureText(text, fs) * PAD;
191
+ const halfAngle = Math.min(arcLen / (2 * rPath), MAX_LABEL_ARC / 2);
192
+ const largeArc = halfAngle * 2 > Math.PI ? 1 : 0;
193
+ const p0x = cx - Math.sin(halfAngle) * rPath;
194
+ const p0y = cy - Math.cos(halfAngle) * rPath;
195
+ const p1x = cx + Math.sin(halfAngle) * rPath;
196
+ const p1y = cy - Math.cos(halfAngle) * rPath;
197
+ const d = `M ${p0x.toFixed(2)} ${p0y.toFixed(2)} A ${rPath.toFixed(2)} ${rPath.toFixed(2)} 0 ${largeArc} 1 ${p1x.toFixed(2)} ${p1y.toFixed(2)}`;
198
+ defs.append("path").attr("id", pathId).attr("d", d);
199
+ const label = labelG.append("text").attr("class", "ring-label").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", fs).attr("font-weight", 600).style("pointer-events", "none");
200
+ const textPath = label.append("textPath").attr("href", `#${pathId}`).attr("startOffset", "50%").attr("text-anchor", "middle");
201
+ renderInlineText(
202
+ textPath,
203
+ text,
204
+ palette,
205
+ fs
206
+ );
207
+ }
163
208
  function renderRingForExport(container, parsed, palette, isDark, exportDims) {
164
209
  renderRing(container, parsed, palette, isDark, void 0, exportDims);
165
210
  }
@@ -248,4 +293,4 @@ export {
248
293
  renderRing,
249
294
  renderRingForExport
250
295
  };
251
- //# sourceMappingURL=chunk-A4VFQH5G.js.map
296
+ //# sourceMappingURL=chunk-YGJZWZTY.js.map