@avodado/render 0.2.2 → 0.2.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.js CHANGED
@@ -3374,6 +3374,28 @@ function rectRoundRight(x2, y, w2, h, r) {
3374
3374
  const rr2 = Math.min(r, w2 / 2, h / 2);
3375
3375
  return `M${x2},${y} h${w2 - rr2} a${rr2},${rr2} 0 0 1 ${rr2},${rr2} v${h - 2 * rr2} a${rr2},${rr2} 0 0 1 ${-rr2},${rr2} h${-(w2 - rr2)} z`;
3376
3376
  }
3377
+ function roundedPath(pts, r = 7) {
3378
+ const round2 = (n) => Math.round(n * 10) / 10;
3379
+ const first = pts[0];
3380
+ if (first === void 0) return "";
3381
+ let d = `M${round2(first.x)},${round2(first.y)}`;
3382
+ for (let i = 1; i < pts.length - 1; i++) {
3383
+ const p2 = pts[i];
3384
+ const a = pts[i - 1];
3385
+ const b = pts[i + 1];
3386
+ if (p2 === void 0 || a === void 0 || b === void 0) continue;
3387
+ const d1 = Math.hypot(p2.x - a.x, p2.y - a.y) || 1;
3388
+ const d2 = Math.hypot(b.x - p2.x, b.y - p2.y) || 1;
3389
+ const r1 = Math.min(r, d1 / 2);
3390
+ const r2 = Math.min(r, d2 / 2);
3391
+ const p1 = { x: p2.x + (a.x - p2.x) / d1 * r1, y: p2.y + (a.y - p2.y) / d1 * r1 };
3392
+ const p22 = { x: p2.x + (b.x - p2.x) / d2 * r2, y: p2.y + (b.y - p2.y) / d2 * r2 };
3393
+ d += ` L${round2(p1.x)},${round2(p1.y)} Q${round2(p2.x)},${round2(p2.y)} ${round2(p22.x)},${round2(p22.y)}`;
3394
+ }
3395
+ const last = pts[pts.length - 1];
3396
+ if (last !== void 0) d += ` L${round2(last.x)},${round2(last.y)}`;
3397
+ return d;
3398
+ }
3377
3399
 
3378
3400
  // src/blocks/c4.ts
3379
3401
  function c4Style(n) {
@@ -3600,55 +3622,48 @@ function umlRel(kind) {
3600
3622
  }
3601
3623
  }
3602
3624
  function renderUml(data) {
3625
+ const classes = data.classes ?? [];
3603
3626
  const rels = data.rels ?? [];
3604
- const classes = ensureGrid(data.classes ?? [], rels, "TB");
3605
- const colW = 150;
3606
- const gapX = 76;
3607
- const gapY = 58;
3608
- const padX = 24;
3609
- const padTop = 28;
3610
- const padBot = 20;
3627
+ const colW = 152;
3611
3628
  const rowH = 14;
3612
3629
  const headH = (c) => 22 + (c.stereotype !== void 0 ? 10 : 0);
3613
3630
  const compH = (list) => (list !== void 0 && list.length > 0 ? list.length * rowH : 6) + 8;
3614
3631
  const clsH = (c) => headH(c) + compH(c.attrs) + compH(c.methods);
3615
- const cols = Math.max(1, ...classes.map((c) => c.col));
3616
- const rows = Math.max(1, ...classes.map((c) => c.row));
3617
- const bandH = /* @__PURE__ */ new Map();
3618
- const bandTop = /* @__PURE__ */ new Map();
3619
- let acc = padTop;
3620
- for (let r = 1; r <= rows; r++) {
3621
- const hs = classes.filter((c) => c.row === r).map(clsH);
3622
- const h = hs.length > 0 ? Math.max(...hs) : 60;
3623
- bandH.set(r, h);
3624
- bandTop.set(r, acc);
3625
- acc += h + gapY;
3626
- }
3627
- const xOf = (c) => padX + (c - 1) * (colW + gapX);
3628
- const rectFor2 = (c) => ({
3629
- x: xOf(c.col),
3630
- y: bandTop.get(c.row) ?? padTop,
3631
- w: colW,
3632
- h: clsH(c)
3633
- });
3634
3632
  const byId = new Map(classes.map((c) => [c.id, c]));
3635
- const width = padX * 2 + cols * colW + (cols - 1) * gapX;
3636
- const height = acc - gapY + padBot;
3637
- let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>UML class diagram</title><defs><marker id="umlTri" viewBox="0 0 14 14" refX="13" refY="7" markerWidth="15" markerHeight="15" orient="auto-start-reverse"><path d="M1,1 L13,7 L1,13 z" fill="#fff" stroke="#1a1a2e" stroke-width="1.2"/></marker><marker id="umlDiaF" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="#1a1a2e"/></marker><marker id="umlDiaH" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="#fff" stroke="#1a1a2e" stroke-width="1.2"/></marker><marker id="umlOpen" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="12" markerHeight="12" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11" fill="none" stroke="#1a1a2e" stroke-width="1.3"/></marker></defs>`;
3633
+ const validRels = rels.filter((rl) => byId.has(rl.from) && byId.has(rl.to));
3634
+ const g = new To.graphlib.Graph({ multigraph: true });
3635
+ g.setGraph({ rankdir: "TB", nodesep: 46, ranksep: 58, marginx: 20, marginy: 20 });
3636
+ g.setDefaultEdgeLabel(() => ({}));
3637
+ for (const c of classes) g.setNode(c.id, { width: colW, height: clsH(c) });
3638
+ validRels.forEach((rl, i) => g.setEdge(rl.from, rl.to, {}, `e${i}`));
3639
+ To.layout(g);
3640
+ const graph = g.graph();
3641
+ const width = Math.ceil(graph.width ?? 0);
3642
+ const height = Math.ceil(graph.height ?? 0);
3643
+ const at2 = /* @__PURE__ */ new Map();
3644
+ for (const c of classes) {
3645
+ const n = g.node(c.id);
3646
+ if (n !== void 0) at2.set(c.id, { x: n.x - colW / 2, y: n.y - clsH(c) / 2 });
3647
+ }
3648
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>UML class diagram</title><defs><marker id="umlTri" viewBox="0 0 14 14" refX="13" refY="7" markerWidth="15" markerHeight="15" orient="auto-start-reverse"><path d="M1,1 L13,7 L1,13 z" fill="var(--white)" stroke="var(--charcoal)" stroke-width="1.2"/></marker><marker id="umlDiaF" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="var(--charcoal)"/></marker><marker id="umlDiaH" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="var(--white)" stroke="var(--charcoal)" stroke-width="1.2"/></marker><marker id="umlOpen" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="12" markerHeight="12" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11" fill="none" stroke="var(--charcoal)" stroke-width="1.3"/></marker></defs>`;
3638
3649
  const labels = [];
3639
- for (const rl of rels) {
3640
- const A2 = byId.get(rl.from);
3641
- const B2 = byId.get(rl.to);
3642
- if (!A2 || !B2) continue;
3643
- const p2 = ortho(rectFor2(A2), rectFor2(B2));
3650
+ validRels.forEach((rl, i) => {
3651
+ const e = g.edge(rl.from, rl.to, `e${i}`);
3652
+ const pts = e?.points;
3653
+ if (pts === void 0 || pts.length < 2) return;
3644
3654
  const st2 = umlRel(rl.kind);
3645
3655
  const start = st2.start !== void 0 ? ` marker-start="url(#${st2.start})"` : "";
3646
3656
  const end = st2.end !== void 0 ? ` marker-end="url(#${st2.end})"` : "";
3647
- s += `<path d="${p2.d}" fill="none" stroke="#1a1a2e" stroke-width="1.3" stroke-dasharray="${st2.dash}"${start}${end}/>`;
3648
- labels.push(edgePill(p2, rl.label));
3649
- }
3657
+ s += `<path d="${roundedPath(pts, 9)}" fill="none" stroke="var(--charcoal)" stroke-width="1.3" stroke-dasharray="${st2.dash}"${start}${end}/>`;
3658
+ if (rl.label !== void 0 && rl.label !== "") {
3659
+ const mid = pts[Math.floor(pts.length / 2)];
3660
+ if (mid !== void 0) labels.push(edgePill({ lx: mid.x, ly: mid.y }, rl.label));
3661
+ }
3662
+ });
3650
3663
  for (const c of classes) {
3651
- const r = rectFor2(c);
3664
+ const p2 = at2.get(c.id);
3665
+ if (p2 === void 0) continue;
3666
+ const r = { x: p2.x, y: p2.y, w: colW, h: clsH(c) };
3652
3667
  const hh = headH(c);
3653
3668
  const aH = compH(c.attrs);
3654
3669
  const nameY = r.y + (c.stereotype !== void 0 ? 24 : 19);
@@ -4047,11 +4062,14 @@ function renderGrid(data) {
4047
4062
  for (const n of nodes) {
4048
4063
  const r = rectFor2(n);
4049
4064
  const st2 = blockStyle(n.kind);
4050
- const gl = nodeGlyph(n.kind, r.x + 16, r.y + 16, st2.accent);
4051
- const nx = gl.length > 0 ? r.x + 42 : r.x + 16;
4052
- const chip = gl.length === 0 && n.kind !== void 0 ? `<text x="${r.x + 16}" y="${r.y + 22}" class="blk-chip" fill="${st2.accent}">${escapeHtml(n.kind)}</text>` : "";
4053
- const tech = n.tech !== void 0 ? `<text x="${nx}" y="${r.y + (gl.length > 0 ? 50 : 60)}" class="blk-tech" fill="${st2.accent}">${escapeHtml(n.tech)}</text>` : "";
4054
- s += `<g filter="url(#gshadow)"><path d="${rectRoundRight(r.x, r.y, r.w, r.h, 9)}" fill="${st2.fill}" stroke="${st2.accent}" stroke-width="1.2"/><rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" fill="${st2.accent}"/>` + gl + chip + `<text x="${nx}" y="${r.y + (gl.length > 0 ? 34 : 44)}" class="blk-name" fill="${st2.text}">${escapeHtml(n.name)}</text>` + tech + `</g>`;
4065
+ const badge = 38;
4066
+ const bx = r.x + 13;
4067
+ const by = r.y + (r.h - badge) / 2;
4068
+ const glyph = nodeGlyph(n.kind, bx + 11, by + 11, "var(--white)");
4069
+ const badgeInner = glyph.length > 0 ? glyph : `<text x="${bx + badge / 2}" y="${by + badge / 2 + 5}" text-anchor="middle" font-family="var(--font-display)" font-size="16" font-weight="700" fill="var(--white)">${escapeHtml((n.name ?? "?").slice(0, 1).toUpperCase())}</text>`;
4070
+ const tx = bx + badge + 13;
4071
+ const hasTech = n.tech !== void 0;
4072
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="12" fill="var(--white)" stroke="var(--rule)" stroke-width="1"/><rect x="${bx}" y="${by}" width="${badge}" height="${badge}" rx="10" fill="${st2.accent}"/>` + badgeInner + `<text x="${tx}" y="${r.y + r.h / 2 + (hasTech ? -3 : 5)}" class="blk-name" fill="var(--charcoal)">${escapeHtml(n.name)}</text>` + (hasTech ? `<text x="${tx}" y="${r.y + r.h / 2 + 14}" class="blk-tech" fill="${st2.accent}">${escapeHtml(n.tech)}</text>` : "") + `</g>`;
4055
4073
  }
4056
4074
  s += labels.join("");
4057
4075
  s += `</svg>`;