@diagrammo/dgmo 0.25.1 → 0.25.3

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.cjs CHANGED
@@ -58685,8 +58685,9 @@ function normalizeSvgForEmbed(input) {
58685
58685
  const h = tight.height + pad2 * 2;
58686
58686
  const canvas = readViewBox(svg);
58687
58687
  const TOL = 2;
58688
- const withinCanvas = !canvas || x >= canvas.x - TOL && y >= canvas.y - TOL && x + w <= canvas.x + canvas.width + TOL && y + h <= canvas.y + canvas.height + TOL;
58689
- if (withinCanvas) {
58688
+ const MIN_COVERAGE = 0.5;
58689
+ const trustworthy = !canvas || x >= canvas.x - TOL && y >= canvas.y - TOL && x + w <= canvas.x + canvas.width + TOL && y + h <= canvas.y + canvas.height + TOL && w >= canvas.width * MIN_COVERAGE && h >= canvas.height * MIN_COVERAGE;
58690
+ if (trustworthy) {
58690
58691
  const vb = `${x} ${y} ${w} ${h}`;
58691
58692
  svg = svg.replace(/(<svg[^>]*?)viewBox="[^"]*"/, `$1viewBox="${vb}"`);
58692
58693
  }
@@ -58781,8 +58782,11 @@ function computeBBox(svg) {
58781
58782
  const y = attr(tag, "y");
58782
58783
  if (x !== null && y !== null) {
58783
58784
  const w = text.length * 7;
58784
- push(x - w / 2, y - 14);
58785
- push(x + w / 2, y + 4);
58785
+ const anchor = tag.match(/\btext-anchor="([^"]*)"/)?.[1] ?? "start";
58786
+ const left = anchor === "middle" ? x - w / 2 : anchor === "end" ? x - w : x;
58787
+ const right = anchor === "middle" ? x + w / 2 : anchor === "end" ? x : x + w;
58788
+ push(left, y - 14);
58789
+ push(right, y + 4);
58786
58790
  }
58787
58791
  }
58788
58792
  for (const m of svg.matchAll(/<path\b[^>]*?\bd="([^"]+)"/g)) {
package/dist/index.js CHANGED
@@ -58690,8 +58690,9 @@ function normalizeSvgForEmbed(input) {
58690
58690
  const h = tight.height + pad2 * 2;
58691
58691
  const canvas = readViewBox(svg);
58692
58692
  const TOL = 2;
58693
- const withinCanvas = !canvas || x >= canvas.x - TOL && y >= canvas.y - TOL && x + w <= canvas.x + canvas.width + TOL && y + h <= canvas.y + canvas.height + TOL;
58694
- if (withinCanvas) {
58693
+ const MIN_COVERAGE = 0.5;
58694
+ const trustworthy = !canvas || x >= canvas.x - TOL && y >= canvas.y - TOL && x + w <= canvas.x + canvas.width + TOL && y + h <= canvas.y + canvas.height + TOL && w >= canvas.width * MIN_COVERAGE && h >= canvas.height * MIN_COVERAGE;
58695
+ if (trustworthy) {
58695
58696
  const vb = `${x} ${y} ${w} ${h}`;
58696
58697
  svg = svg.replace(/(<svg[^>]*?)viewBox="[^"]*"/, `$1viewBox="${vb}"`);
58697
58698
  }
@@ -58786,8 +58787,11 @@ function computeBBox(svg) {
58786
58787
  const y = attr(tag, "y");
58787
58788
  if (x !== null && y !== null) {
58788
58789
  const w = text.length * 7;
58789
- push(x - w / 2, y - 14);
58790
- push(x + w / 2, y + 4);
58790
+ const anchor = tag.match(/\btext-anchor="([^"]*)"/)?.[1] ?? "start";
58791
+ const left = anchor === "middle" ? x - w / 2 : anchor === "end" ? x - w : x;
58792
+ const right = anchor === "middle" ? x + w / 2 : anchor === "end" ? x : x + w;
58793
+ push(left, y - 14);
58794
+ push(right, y + 4);
58791
58795
  }
58792
58796
  }
58793
58797
  for (const m of svg.matchAll(/<path\b[^>]*?\bd="([^"]+)"/g)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -157,9 +157,9 @@
157
157
  "d3-scale": "^4.0.2",
158
158
  "d3-selection": "^3.0.0",
159
159
  "d3-shape": "^3.2.0",
160
- "echarts": "^6.0.0",
160
+ "echarts": "^6.1.0",
161
161
  "elkjs": "^0.11.1",
162
- "jsdom": "^29.0.2",
162
+ "jsdom": "^29.1.1",
163
163
  "lz-string": "^1.5.0",
164
164
  "topojson-client": "^3.1.0"
165
165
  },
@@ -175,30 +175,30 @@
175
175
  "@types/d3-scale": "^4.0.9",
176
176
  "@types/d3-selection": "^3.0.11",
177
177
  "@types/d3-shape": "^3.1.8",
178
- "@types/jsdom": "^28.0.1",
178
+ "@types/jsdom": "^28.0.3",
179
179
  "@types/topojson-client": "^3.1.5",
180
- "@vitest/coverage-v8": "^4.1.6",
181
- "axe-core": "^4.10.0",
182
- "cspell": "^10.0.0",
180
+ "@vitest/coverage-v8": "^4.1.8",
181
+ "axe-core": "^4.12.0",
182
+ "cspell": "^10.0.1",
183
183
  "depcheck": "^1.4.7",
184
184
  "esbuild": "^0.28.0",
185
- "eslint": "^10.2.0",
185
+ "eslint": "^10.4.1",
186
186
  "eslint-plugin-security": "^4.0.0",
187
- "fast-check": "^4.7.0",
187
+ "fast-check": "^4.8.0",
188
188
  "fflate": "^0.8.3",
189
189
  "husky": "^9.1.7",
190
- "jscpd": "^4.0.9",
191
- "knip": "^6.3.1",
192
- "lint-staged": "^16.4.0",
190
+ "jscpd": "^4.2.4",
191
+ "knip": "^6.15.0",
192
+ "lint-staged": "^17.0.7",
193
193
  "madge": "^8.0.0",
194
194
  "mapshaper": "0.7.22",
195
- "prettier": "^3.8.2",
196
- "publint": "^0.3.18",
195
+ "prettier": "^3.8.3",
196
+ "publint": "^0.3.21",
197
197
  "tsup": "^8.5.1",
198
198
  "type-coverage": "^2.29.7",
199
- "typescript": "^6.0.2",
200
- "typescript-eslint": "^8.58.1",
201
- "vitest": "^4.1.4"
199
+ "typescript": "^6.0.3",
200
+ "typescript-eslint": "^8.60.1",
201
+ "vitest": "^4.1.8"
202
202
  },
203
203
  "lint-staged": {
204
204
  "*.ts": [
@@ -38,23 +38,30 @@ export function normalizeSvgForEmbed(input: string): string {
38
38
  const y = tight.y - pad;
39
39
  const w = tight.width + pad * 2;
40
40
  const h = tight.height + pad * 2;
41
- // Genuine content-tightening is ALWAYS a sub-rectangle of the renderer's
42
- // export canvas. computeBBox is a best-effort string parser: it misreads
43
- // path arc commands (`A rx ry rot x y`) and ignores `<g transform>`, so
44
- // it can return a box that strays OUTSIDE the canvas (negative origin or
45
- // over-wide). That's a parse error, not real content applying it shifts the
46
- // viewBox and CLIPS the diagram (the right/bottom fall off in embeds). Only
47
- // tighten when the computed box sits within the renderer's viewBox;
48
- // otherwise keep the already-correct canvas bounds.
41
+ // The renderer's viewBox is authoritative; computeBBox may only shave dead
42
+ // margins off it. computeBBox is a best-effort string parser with two
43
+ // failure modes, and the renderer's canvas bounds catch both:
44
+ // 1. OVER-shoot — it misreads path arc commands (`A rx ry rot x y`) and
45
+ // yields a box that strays OUTSIDE the canvas (negative origin /
46
+ // over-wide). Applying it shifts the viewBox and CLIPS the diagram.
47
+ // 2. UNDER-shoot it can't see elements positioned via `transform`
48
+ // (no x/y attrs), e.g. word-cloud words. It then measures only the
49
+ // stragglers (the title) and collapses the box to a tiny region,
50
+ // zooming that fragment to fill the frame.
51
+ // So only tighten when the computed box sits WITHIN the canvas AND still
52
+ // covers most of it; otherwise keep the renderer's correct bounds.
49
53
  const canvas = readViewBox(svg);
50
54
  const TOL = 2;
51
- const withinCanvas =
55
+ const MIN_COVERAGE = 0.5; // a real trim shaves margins, not the diagram
56
+ const trustworthy =
52
57
  !canvas ||
53
58
  (x >= canvas.x - TOL &&
54
59
  y >= canvas.y - TOL &&
55
60
  x + w <= canvas.x + canvas.width + TOL &&
56
- y + h <= canvas.y + canvas.height + TOL);
57
- if (withinCanvas) {
61
+ y + h <= canvas.y + canvas.height + TOL &&
62
+ w >= canvas.width * MIN_COVERAGE &&
63
+ h >= canvas.height * MIN_COVERAGE);
64
+ if (trustworthy) {
58
65
  const vb = `${x} ${y} ${w} ${h}`;
59
66
  svg = svg.replace(/(<svg[^>]*?)viewBox="[^"]*"/, `$1viewBox="${vb}"`);
60
67
  }
@@ -87,26 +94,34 @@ export function getEmbedSvgViewBox(
87
94
  };
88
95
  }
89
96
 
90
- /**
91
- * Compute an approximate content bounding box from raw element coordinates.
92
- *
93
- * This is a regex walk, not a real SVG layout — it ignores `transform`
94
- * attributes and uses a heuristic for text widths. dgmo's renderers mostly use
95
- * absolute coordinates within their viewBox, so the approximation is close
96
- * enough that the rendered output reliably fills the visible area.
97
- */
98
97
  /** Read the root `<svg>` viewBox as numbers, if present and well-formed. */
99
98
  function readViewBox(
100
99
  svg: string
101
100
  ): { x: number; y: number; width: number; height: number } | null {
102
101
  const m = svg.match(/<svg[^>]*?\bviewBox="([^"]+)"/);
103
102
  if (!m) return null;
104
- const n = m[1]!.trim().split(/[\s,]+/).map(Number);
105
- if (n.length !== 4 || n.some((v) => !Number.isFinite(v)) || n[2]! <= 0 || n[3]! <= 0)
103
+ const n = m[1]!
104
+ .trim()
105
+ .split(/[\s,]+/)
106
+ .map(Number);
107
+ if (
108
+ n.length !== 4 ||
109
+ n.some((v) => !Number.isFinite(v)) ||
110
+ n[2]! <= 0 ||
111
+ n[3]! <= 0
112
+ )
106
113
  return null;
107
114
  return { x: n[0]!, y: n[1]!, width: n[2]!, height: n[3]! };
108
115
  }
109
116
 
117
+ /**
118
+ * Compute an approximate content bounding box from raw element coordinates.
119
+ *
120
+ * This is a regex walk, not a real SVG layout — it ignores `transform`
121
+ * attributes and uses a heuristic for text widths. dgmo's renderers mostly use
122
+ * absolute coordinates within their viewBox, so the approximation is close
123
+ * enough that the rendered output reliably fills the visible area.
124
+ */
110
125
  function computeBBox(
111
126
  svg: string
112
127
  ): { x: number; y: number; width: number; height: number } | null {
@@ -189,9 +204,18 @@ function computeBBox(
189
204
  const y = attr(tag, 'y');
190
205
  if (x !== null && y !== null) {
191
206
  const w = text.length * 7;
192
- // text-anchor may be start/middle/end; assume worst case (middle).
193
- push(x - w / 2, y - 14);
194
- push(x + w / 2, y + 4);
207
+ // Honor text-anchor so the horizontal extent points the right way:
208
+ // `start` (SVG default) grows rightward from x, `end` grows leftward,
209
+ // `middle` straddles x. Assuming middle for everything under-measures
210
+ // start-anchored text (e.g. pyramid right-column descriptions), which
211
+ // collapses the tight viewBox and clips that text in embeds.
212
+ const anchor = tag.match(/\btext-anchor="([^"]*)"/)?.[1] ?? 'start';
213
+ const left =
214
+ anchor === 'middle' ? x - w / 2 : anchor === 'end' ? x - w : x;
215
+ const right =
216
+ anchor === 'middle' ? x + w / 2 : anchor === 'end' ? x : x + w;
217
+ push(left, y - 14);
218
+ push(right, y + 4);
195
219
  }
196
220
  }
197
221