vizo-mcp 0.1.0 → 0.3.0

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 (47) hide show
  1. package/README.md +15 -2
  2. package/dist/fonts/CascadiaCode-Regular.ttf +0 -0
  3. package/dist/fonts/CascadiaCode-Regular.woff2 +0 -0
  4. package/dist/fonts/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.ttf +0 -0
  5. package/dist/fonts/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
  6. package/dist/fonts/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.ttf +0 -0
  7. package/dist/fonts/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
  8. package/dist/fonts/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.ttf +0 -0
  9. package/dist/fonts/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
  10. package/dist/fonts/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.ttf +0 -0
  11. package/dist/fonts/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
  12. package/dist/fonts/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.ttf +0 -0
  13. package/dist/fonts/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
  14. package/dist/fonts/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.ttf +0 -0
  15. package/dist/fonts/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
  16. package/dist/fonts/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.ttf +0 -0
  17. package/dist/fonts/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
  18. package/dist/fonts/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.ttf +0 -0
  19. package/dist/fonts/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
  20. package/dist/fonts/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.ttf +0 -0
  21. package/dist/fonts/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
  22. package/dist/fonts/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.ttf +0 -0
  23. package/dist/fonts/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
  24. package/dist/fonts/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.ttf +0 -0
  25. package/dist/fonts/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
  26. package/dist/fonts/LiberationSans-Regular.ttf +0 -0
  27. package/dist/fonts/LiberationSans-Regular.woff2 +0 -0
  28. package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.ttf +0 -0
  29. package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
  30. package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.ttf +0 -0
  31. package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
  32. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.ttf +0 -0
  33. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
  34. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.ttf +0 -0
  35. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
  36. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.ttf +0 -0
  37. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
  38. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.ttf +0 -0
  39. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
  40. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.ttf +0 -0
  41. package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
  42. package/dist/fonts/Virgil-Regular.ttf +0 -0
  43. package/dist/fonts/Virgil-Regular.woff2 +0 -0
  44. package/dist/fonts/fonts.json +146 -0
  45. package/dist/index.js +2986 -164
  46. package/fonts/README.md +15 -0
  47. package/package.json +13 -9
package/dist/index.js CHANGED
@@ -15,6 +15,8 @@ import {
15
15
  ListToolsRequestSchema,
16
16
  ReadResourceRequestSchema
17
17
  } from "@modelcontextprotocol/sdk/types.js";
18
+ import { readFileSync as readFileSync3, statSync } from "node:fs";
19
+ import { resolve as resolve2 } from "node:path";
18
20
 
19
21
  // ../diagram/src/schema.ts
20
22
  import { z } from "zod";
@@ -41,11 +43,16 @@ var Group = z.object({
41
43
  children: z.array(id).describe("Node ids (or nested group ids) inside this group"),
42
44
  color: Color.optional()
43
45
  });
46
+ var nodeImage = {
47
+ image: z.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/, "image must be a fileId").optional().describe("fileId of an image on this board (from add_image, or an image's fileId in get_board). The node becomes a card: picture above the label."),
48
+ imageSize: z.number().min(16).max(800).optional().describe("Longer side of the picture in board units (default 96)")
49
+ };
44
50
  var FlowchartNode = z.object({
45
51
  id,
46
52
  label,
47
- shape: z.enum(["process", "decision", "terminal", "io", "database", "subroutine", "note"]).optional().describe("process (default) rectangle; decision diamond; terminal start/end pill; database; note sticky"),
48
- color: Color.optional()
53
+ shape: z.enum(["process", "decision", "terminal", "io", "database", "subroutine", "note"]).optional().describe("process (default) rectangle; decision diamond; terminal start/end pill; database; note sticky. Nodes with an image are always rectangles."),
54
+ color: Color.optional(),
55
+ ...nodeImage
49
56
  });
50
57
  var FlowchartSpec = z.object({
51
58
  kind: z.literal("flowchart"),
@@ -61,7 +68,8 @@ var ComponentNode = z.object({
61
68
  type: z.enum(["service", "database", "queue", "cache", "client", "external", "storage", "function", "user", "generic"]).optional().describe("Drives the shape and color"),
62
69
  tech: z.string().max(60).optional().describe("Technology caption, e.g. 'PostgreSQL', 'React'"),
63
70
  description: z.string().max(200).optional(),
64
- color: Color.optional()
71
+ color: Color.optional(),
72
+ ...nodeImage
65
73
  });
66
74
  var ComponentSpec = z.object({
67
75
  kind: z.literal("component"),
@@ -159,6 +167,10 @@ var DiagramSpec = z.discriminatedUnion("kind", [
159
167
  StateSpec,
160
168
  MindmapSpec
161
169
  ]);
170
+ function specImageIds(spec) {
171
+ const nodes = spec.kind === "flowchart" || spec.kind === "component" ? spec.nodes : [];
172
+ return [...new Set(nodes.flatMap((n) => n.image ? [n.image] : []))];
173
+ }
162
174
  function validateSpec(spec) {
163
175
  const errors = [];
164
176
  const check = (ids, refs, what, groups) => {
@@ -245,7 +257,7 @@ function measureLine(text2, fontSize, font) {
245
257
  }
246
258
  function measureText(text2, fontSize, font) {
247
259
  const lines = text2.split("\n");
248
- const width = Math.max(0, ...lines.map((l) => measureLine(l, fontSize, font)));
260
+ const width = Math.max(0, ...lines.map((l2) => measureLine(l2, fontSize, font)));
249
261
  const height = lines.length * fontSize * LINE_HEIGHT[font];
250
262
  return { width: Math.ceil(width), height: Math.ceil(height), lines: lines.length };
251
263
  }
@@ -253,17 +265,17 @@ function wrapText(text2, fontSize, font, maxWidth) {
253
265
  const out = [];
254
266
  for (const para of text2.split("\n")) {
255
267
  const words = para.split(/\s+/).filter(Boolean);
256
- let line = "";
268
+ let line2 = "";
257
269
  for (const word of words) {
258
- const candidate = line ? `${line} ${word}` : word;
259
- if (!line || measureLine(candidate, fontSize, font) <= maxWidth) {
260
- line = candidate;
270
+ const candidate = line2 ? `${line2} ${word}` : word;
271
+ if (!line2 || measureLine(candidate, fontSize, font) <= maxWidth) {
272
+ line2 = candidate;
261
273
  } else {
262
- out.push(line);
263
- line = word;
274
+ out.push(line2);
275
+ line2 = word;
264
276
  }
265
277
  }
266
- out.push(line);
278
+ out.push(line2);
267
279
  }
268
280
  return out.join("\n");
269
281
  }
@@ -302,7 +314,7 @@ var randomNonce = () => Math.floor(Math.random() * 2 ** 31);
302
314
  function randomId(len = 20) {
303
315
  const abc = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
304
316
  const bytes = crypto.getRandomValues(new Uint8Array(len));
305
- return Array.from(bytes, (b) => abc[b % abc.length]).join("");
317
+ return Array.from(bytes, (b2) => abc[b2 % abc.length]).join("");
306
318
  }
307
319
  function base(id2, type, x, y, width, height, o = {}) {
308
320
  return {
@@ -401,23 +413,23 @@ function arrow(id2, pts, o = {}) {
401
413
  const bind = (target, p) => {
402
414
  if (!target) return null;
403
415
  target.boundElements = [...target.boundElements ?? [], { id: id2, type: "arrow" }];
404
- const b = { elementId: target.id, focus: 0, gap: 4 };
416
+ const b2 = { elementId: target.id, focus: 0, gap: 4 };
405
417
  if (o.elbowed) {
406
- b.fixedPoint = [
418
+ b2.fixedPoint = [
407
419
  clamp((p[0] - target.x) / (target.width || 1), 0, 1),
408
420
  clamp((p[1] - target.y) / (target.height || 1), 0, 1)
409
421
  ];
410
- b.gap = 0;
422
+ b2.gap = 0;
411
423
  }
412
- return b;
424
+ return b2;
413
425
  };
414
426
  el.startBinding = bind(o.start, clean[0]);
415
427
  el.endBinding = bind(o.end, clean[clean.length - 1]);
416
428
  return el;
417
429
  }
418
- function arrowLabel(a, value, o = {}) {
419
- const t = text(`${a.id}.t`, 0, 0, value, { textAlign: "center", verticalAlign: "middle", fontSize: 16, ...o });
420
- const pts = a.points;
430
+ function arrowLabel(a2, value, o = {}) {
431
+ const t = text(`${a2.id}.t`, 0, 0, value, { textAlign: "center", verticalAlign: "middle", fontSize: 16, ...o });
432
+ const pts = a2.points;
421
433
  let mid;
422
434
  if (pts.length % 2 === 1) mid = pts[Math.floor(pts.length / 2)];
423
435
  else {
@@ -425,14 +437,23 @@ function arrowLabel(a, value, o = {}) {
425
437
  const q = pts[pts.length / 2];
426
438
  mid = [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2];
427
439
  }
428
- t.containerId = a.id;
429
- t.x = round(a.x + mid[0] - t.width / 2);
430
- t.y = round(a.y + mid[1] - t.height / 2);
431
- t.groupIds = a.groupIds ?? [];
432
- if (a.customData) t.customData = { ...a.customData, role: "label" };
433
- a.boundElements = [...a.boundElements ?? [], { id: t.id, type: "text" }];
440
+ t.containerId = a2.id;
441
+ t.x = round(a2.x + mid[0] - t.width / 2);
442
+ t.y = round(a2.y + mid[1] - t.height / 2);
443
+ t.groupIds = a2.groupIds ?? [];
444
+ if (a2.customData) t.customData = { ...a2.customData, role: "label" };
445
+ a2.boundElements = [...a2.boundElements ?? [], { id: t.id, type: "text" }];
434
446
  return t;
435
447
  }
448
+ function image(id2, x, y, w, h, fileId, o = {}) {
449
+ return {
450
+ ...base(id2, "image", x, y, w, h, { strokeColor: "transparent", ...o }),
451
+ fileId,
452
+ status: "saved",
453
+ scale: [1, 1],
454
+ crop: null
455
+ };
456
+ }
436
457
  function frame(id2, x, y, w, h, name, o = {}) {
437
458
  return { ...base(id2, "frame", x, y, w, h, { roughness: 0, strokeColor: "#bbb", ...o }), roundness: null, name };
438
459
  }
@@ -445,18 +466,18 @@ function dedupe(pts) {
445
466
  }
446
467
  const res = [];
447
468
  for (let i = 0; i < out.length; i++) {
448
- const a = res[res.length - 1];
449
- const b = out[i];
469
+ const a2 = res[res.length - 1];
470
+ const b2 = out[i];
450
471
  const c = out[i + 1];
451
- if (a && c && (Math.abs(a[0] - b[0]) < 0.5 && Math.abs(b[0] - c[0]) < 0.5 || Math.abs(a[1] - b[1]) < 0.5 && Math.abs(b[1] - c[1]) < 0.5))
472
+ if (a2 && c && (Math.abs(a2[0] - b2[0]) < 0.5 && Math.abs(b2[0] - c[0]) < 0.5 || Math.abs(a2[1] - b2[1]) < 0.5 && Math.abs(b2[1] - c[1]) < 0.5))
452
473
  continue;
453
- res.push(b);
474
+ res.push(b2);
454
475
  }
455
476
  return res.length >= 2 ? res : [out[0], [out[0][0] + 1, out[0][1]]];
456
477
  }
457
478
  var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
458
479
  function elementBounds(els) {
459
- let b = null;
480
+ let b2 = null;
460
481
  for (const e of els) {
461
482
  if (e.isDeleted) continue;
462
483
  let x1 = e.x, y1 = e.y, x2 = e.x + e.width, y2 = e.y + e.height;
@@ -468,15 +489,15 @@ function elementBounds(els) {
468
489
  y1 = Math.min(...ys);
469
490
  y2 = Math.max(...ys);
470
491
  }
471
- if (!b) b = { minX: x1, minY: y1, maxX: x2, maxY: y2 };
492
+ if (!b2) b2 = { minX: x1, minY: y1, maxX: x2, maxY: y2 };
472
493
  else {
473
- b.minX = Math.min(b.minX, x1);
474
- b.minY = Math.min(b.minY, y1);
475
- b.maxX = Math.max(b.maxX, x2);
476
- b.maxY = Math.max(b.maxY, y2);
494
+ b2.minX = Math.min(b2.minX, x1);
495
+ b2.minY = Math.min(b2.minY, y1);
496
+ b2.maxX = Math.max(b2.maxX, x2);
497
+ b2.maxY = Math.max(b2.maxY, y2);
477
498
  }
478
499
  }
479
- return b;
500
+ return b2;
480
501
  }
481
502
  function translate(els, dx, dy) {
482
503
  for (const e of els) {
@@ -567,7 +588,7 @@ async function layoutGraph(m) {
567
588
  for (let p = parentOf.get(id2); p; p = parentOf.get(p)) d++;
568
589
  return d;
569
590
  };
570
- for (const g of [...m.groups].sort((a, b) => depth(a.id) - depth(b.id))) {
591
+ for (const g of [...m.groups].sort((a2, b2) => depth(a2.id) - depth(b2.id))) {
571
592
  const p = pos.get(g.id);
572
593
  const c = PALETTE[g.color ?? "gray"];
573
594
  const gid = `${m.idPrefix}.g.${g.id}`;
@@ -619,7 +640,7 @@ async function layoutGraph(m) {
619
640
  ];
620
641
  } else continue;
621
642
  const isSelf = e.from === e.to;
622
- const a = arrow(`${m.idPrefix}.e.${e.id}`, pts, {
643
+ const a2 = arrow(`${m.idPrefix}.e.${e.id}`, pts, {
623
644
  start: from,
624
645
  end: to,
625
646
  startArrowhead: e.startArrowhead ?? null,
@@ -629,8 +650,8 @@ async function layoutGraph(m) {
629
650
  elbowed: m.elbowed && !isSelf && pts.length > 2,
630
651
  customData: m.customData("edge", e.id)
631
652
  });
632
- edgeEls.push(a);
633
- if (e.label) edgeEls.push(arrowLabel(a, e.label, { font: m.look.font }));
653
+ edgeEls.push(a2);
654
+ if (e.label) edgeEls.push(arrowLabel(a2, e.label, { font: m.look.font }));
634
655
  const endLabel = (lbl, p, q, role) => {
635
656
  if (!lbl) return;
636
657
  const dx = Math.sign(q[0] - p[0]);
@@ -639,7 +660,7 @@ async function layoutGraph(m) {
639
660
  const x = p[0] + (dx !== 0 ? dx * 8 + (dx < 0 ? -m2.width : 0) : 8);
640
661
  const y = p[1] + (dy !== 0 ? dy * 6 + (dy < 0 ? -m2.height : 0) : -m2.height - 4);
641
662
  edgeEls.push(
642
- text(`${a.id}.${role}`, x, y, lbl, { fontSize: 14, font: m.look.font, customData: m.customData(role, e.id) })
663
+ text(`${a2.id}.${role}`, x, y, lbl, { fontSize: 14, font: m.look.font, customData: m.customData(role, e.id) })
643
664
  );
644
665
  };
645
666
  endLabel(e.fromLabel, pts[0], pts[1], "from-mult");
@@ -677,6 +698,43 @@ function labelledNode(ctx, nodeId, label2, kind, colors, opts = {}) {
677
698
  }
678
699
  };
679
700
  }
701
+ var CARD_PAD = 10;
702
+ var CARD_GAP = 8;
703
+ var BOUND_TEXT_PADDING = 5;
704
+ function imageCard(ctx, nodeId, label2, fileId, size, colors, opts = {}) {
705
+ const fontSize = opts.fontSize ?? NODE_FONT;
706
+ const natural = ctx.images[fileId];
707
+ const k = size / Math.max(natural.width, natural.height);
708
+ const iw = Math.round(natural.width * k);
709
+ const ih = Math.round(natural.height * k);
710
+ const wrapped = wrapText(label2, fontSize, ctx.look.font, Math.max(MAX_LABEL_W, iw));
711
+ const m = measureText(wrapped, fontSize, ctx.look.font);
712
+ const w = Math.ceil(Math.max(opts.minW ?? 120, iw + CARD_PAD * 2, m.width + 10 + CARD_PAD * 2));
713
+ const h = Math.ceil(CARD_PAD + ih + CARD_GAP + m.height + BOUND_TEXT_PADDING);
714
+ const id2 = `${ctx.idPrefix}.n.${nodeId}`;
715
+ return {
716
+ id: nodeId,
717
+ width: w,
718
+ height: h,
719
+ bindId: id2,
720
+ render: (x, y) => {
721
+ const groupIds = [`${id2}.grp`];
722
+ const s = shape("rectangle", id2, x, y, w, h, {
723
+ strokeColor: colors.stroke,
724
+ backgroundColor: colors.bg,
725
+ strokeStyle: opts.strokeStyle,
726
+ roughness: ctx.look.roughness,
727
+ groupIds,
728
+ customData: ctx.cd("node", nodeId)
729
+ });
730
+ const pic = image(`${id2}.img`, x + (w - iw) / 2, y + CARD_PAD, iw, ih, fileId, { groupIds, customData: ctx.cd("node-image", nodeId) });
731
+ const t = boundText(s, wrapped, { fontSize, font: ctx.look.font, strokeColor: INK, verticalAlign: "bottom" });
732
+ t.y = y + h - BOUND_TEXT_PADDING - t.height;
733
+ return [s, pic, t];
734
+ }
735
+ };
736
+ }
737
+ var DEFAULT_IMAGE_SIZE = 96;
680
738
  var fill = (c) => ({ stroke: INK, bg: PALETTE[c][1] });
681
739
  function flowchartModel(spec, ctx) {
682
740
  const nodes = spec.nodes.map((n) => {
@@ -691,6 +749,11 @@ function flowchartModel(spec, ctx) {
691
749
  note: ["rectangle", "yellow"]
692
750
  };
693
751
  const [kind, color] = def[shp];
752
+ if (n.image)
753
+ return imageCard(ctx, n.id, n.label, n.image, n.imageSize ?? DEFAULT_IMAGE_SIZE, fill(n.color ?? color), {
754
+ strokeStyle: shp === "note" ? "dashed" : "solid",
755
+ fontSize: shp === "note" ? 16 : NODE_FONT
756
+ });
694
757
  return labelledNode(ctx, n.id, n.label, kind, fill(n.color ?? color), {
695
758
  strokeStyle: shp === "note" ? "dashed" : "solid",
696
759
  fontSize: shp === "note" ? 16 : NODE_FONT
@@ -725,6 +788,12 @@ function componentModel(spec, ctx) {
725
788
  if (n.tech) lines.push(`[${n.tech}]`);
726
789
  else if (st.caption) lines.push(`\xAB${st.caption}\xBB`);
727
790
  if (n.description) lines.push(wrapText(n.description, 16, ctx.look.font, MAX_LABEL_W));
791
+ if (n.image)
792
+ return imageCard(ctx, n.id, lines.join("\n"), n.image, n.imageSize ?? DEFAULT_IMAGE_SIZE, fill(n.color ?? st.color), {
793
+ strokeStyle: st.dashed ? "dashed" : "solid",
794
+ fontSize: n.description ? 16 : 18,
795
+ minW: 160
796
+ });
728
797
  return labelledNode(ctx, n.id, lines.join("\n"), st.kind, fill(n.color ?? st.color), {
729
798
  strokeStyle: st.dashed ? "dashed" : "solid",
730
799
  fontSize: n.description ? 16 : 18,
@@ -754,10 +823,10 @@ function classModel(spec, ctx) {
754
823
  const nodes = spec.classes.map((c) => {
755
824
  const header = (c.stereotype ? `\xAB${c.stereotype}\xBB
756
825
  ` : "") + c.name;
757
- const attrs = (c.attributes ?? []).join("\n");
826
+ const attrs2 = (c.attributes ?? []).join("\n");
758
827
  const meths = (c.methods ?? []).join("\n");
759
828
  const hM = measureText(header, 20, font);
760
- const aM = attrs ? measureText(attrs, 16, font) : { width: 0, height: 20 };
829
+ const aM = attrs2 ? measureText(attrs2, 16, font) : { width: 0, height: 20 };
761
830
  const mM = meths ? measureText(meths, 16, font) : { width: 0, height: 20 };
762
831
  const padX = 14;
763
832
  const w = Math.max(180, hM.width + padX * 2 + 10, aM.width + padX * 2, mM.width + padX * 2);
@@ -802,9 +871,9 @@ function classModel(spec, ctx) {
802
871
  [x + w, y + hH + aH]
803
872
  ], { kind: "line", roughness: ctx.look.roughness, groupIds: group, strokeWidth: 1 });
804
873
  els.push(div);
805
- if (attrs)
874
+ if (attrs2)
806
875
  els.push(
807
- text(`${id2}.attrs`, x + padX, y + hH + 8, attrs, { fontSize: 16, font, groupIds: group, customData: ctx.cd("class-attrs", c.id) })
876
+ text(`${id2}.attrs`, x + padX, y + hH + 8, attrs2, { fontSize: 16, font, groupIds: group, customData: ctx.cd("class-attrs", c.id) })
808
877
  );
809
878
  if (meths)
810
879
  els.push(
@@ -931,15 +1000,15 @@ function placeRadial(root) {
931
1000
  const ring = Math.max(220, maxW(root) + 80);
932
1001
  const rec = (t, a0, a1, depth) => {
933
1002
  const total = leaves(t);
934
- let a = a0;
1003
+ let a2 = a0;
935
1004
  for (const c of t.children) {
936
1005
  const share = (a1 - a0) * leaves(c) / total;
937
- const mid = a + share / 2;
1006
+ const mid = a2 + share / 2;
938
1007
  const r = ring * depth + (depth === 1 ? root.w / 4 : 0);
939
1008
  c.x = root.x + root.w / 2 + Math.cos(mid) * r - c.w / 2;
940
1009
  c.y = root.y + root.h / 2 + Math.sin(mid) * r - c.h / 2;
941
- rec(c, a, a + share, depth + 1);
942
- a += share;
1010
+ rec(c, a2, a2 + share, depth + 1);
1011
+ a2 += share;
943
1012
  }
944
1013
  };
945
1014
  rec(root, -Math.PI / 2, 3 * Math.PI / 2, 1);
@@ -1024,8 +1093,8 @@ function edgePoint(e, target) {
1024
1093
  const dy = target[1] - cy;
1025
1094
  if (dx === 0 && dy === 0) return [cx, cy];
1026
1095
  if (e.type === "ellipse") {
1027
- const a = e.width / 2, b = e.height / 2;
1028
- const t2 = 1 / Math.sqrt(dx * dx / (a * a) + dy * dy / (b * b));
1096
+ const a2 = e.width / 2, b2 = e.height / 2;
1097
+ const t2 = 1 / Math.sqrt(dx * dx / (a2 * a2) + dy * dy / (b2 * b2));
1029
1098
  return [cx + dx * t2, cy + dy * t2];
1030
1099
  }
1031
1100
  const t = Math.min(e.width / 2 / Math.abs(dx || 1e-9), e.height / 2 / Math.abs(dy || 1e-9));
@@ -1047,24 +1116,24 @@ function layoutSequence(spec, ctx) {
1047
1116
  const fit = fitContainer(kind, m.width, m.height, 18, 10);
1048
1117
  return { lbl, kind, w: Math.max(130, fit.w), h: Math.max(HEAD_H, fit.h) };
1049
1118
  });
1050
- const headH = Math.max(...boxes.map((b) => b.h));
1119
+ const headH = Math.max(...boxes.map((b2) => b2.h));
1051
1120
  const gaps = P.map((_, i) => i === 0 ? 0 : Math.max(boxes[i - 1].w / 2 + boxes[i].w / 2 + 50, 180));
1052
1121
  const msgLabels = spec.messages.map((m) => m.label ? wrapText(m.label, MSG_FONT, font, 320) : "");
1053
- const order = spec.messages.map((_, i) => i).sort((a, b) => span(a) - span(b));
1122
+ const order = spec.messages.map((_, i) => i).sort((a2, b2) => span(a2) - span(b2));
1054
1123
  function span(i) {
1055
1124
  const m = spec.messages[i];
1056
1125
  return Math.abs(idx.get(m.from) - idx.get(m.to));
1057
1126
  }
1058
1127
  for (const i of order) {
1059
1128
  const m = spec.messages[i];
1060
- const a = idx.get(m.from);
1061
- const b = idx.get(m.to);
1129
+ const a2 = idx.get(m.from);
1130
+ const b2 = idx.get(m.to);
1062
1131
  const lw = msgLabels[i] ? measureText(msgLabels[i], MSG_FONT, font).width : 0;
1063
- if (a === b) {
1064
- if (a + 1 < P.length) gaps[a + 1] = Math.max(gaps[a + 1], lw + 90);
1132
+ if (a2 === b2) {
1133
+ if (a2 + 1 < P.length) gaps[a2 + 1] = Math.max(gaps[a2 + 1], lw + 90);
1065
1134
  continue;
1066
1135
  }
1067
- const [lo, hi] = a < b ? [a, b] : [b, a];
1136
+ const [lo, hi] = a2 < b2 ? [a2, b2] : [b2, a2];
1068
1137
  let cur = 0;
1069
1138
  for (let k = lo + 1; k <= hi; k++) cur += gaps[k];
1070
1139
  const need = lw + 60;
@@ -1108,8 +1177,8 @@ function layoutSequence(spec, ctx) {
1108
1177
  emitNotes(-1);
1109
1178
  spec.messages.forEach((m, i) => {
1110
1179
  y += (fragStarts.get(i) ?? 0) * FRAG_HEADER;
1111
- const a = idx.get(m.from);
1112
- const b = idx.get(m.to);
1180
+ const a2 = idx.get(m.from);
1181
+ const b2 = idx.get(m.to);
1113
1182
  const lbl = msgLabels[i];
1114
1183
  const lm = lbl ? measureText(lbl, MSG_FONT, font) : { width: 0, height: 0 };
1115
1184
  const top = y;
@@ -1123,29 +1192,29 @@ function layoutSequence(spec, ctx) {
1123
1192
  groupIds: grp,
1124
1193
  customData: ctx.cd("message", String(i))
1125
1194
  };
1126
- if (a === b) {
1195
+ if (a2 === b2) {
1127
1196
  const ay = y + Math.max(lm.height - 6, 0);
1128
1197
  els.push(
1129
1198
  arrow(
1130
1199
  id2,
1131
1200
  [
1132
- [cx[a], ay],
1133
- [cx[a] + 50, ay],
1134
- [cx[a] + 50, ay + 34],
1135
- [cx[a] + 4, ay + 34]
1201
+ [cx[a2], ay],
1202
+ [cx[a2] + 50, ay],
1203
+ [cx[a2] + 50, ay + 34],
1204
+ [cx[a2] + 4, ay + 34]
1136
1205
  ],
1137
1206
  common2
1138
1207
  )
1139
1208
  );
1140
- if (lbl) els.push(text(`${id2}.t`, cx[a] + 60, ay + 17 - lm.height / 2, lbl, { fontSize: MSG_FONT, font, groupIds: grp, customData: ctx.cd("message-label", String(i)) }));
1209
+ if (lbl) els.push(text(`${id2}.t`, cx[a2] + 60, ay + 17 - lm.height / 2, lbl, { fontSize: MSG_FONT, font, groupIds: grp, customData: ctx.cd("message-label", String(i)) }));
1141
1210
  y = ay + 34 + 26;
1142
1211
  } else {
1143
1212
  const ay = y + lm.height + 6;
1144
- const dir = Math.sign(cx[b] - cx[a]);
1145
- els.push(arrow(id2, [[cx[a], ay], [cx[b] - dir * 2, ay]], common2));
1213
+ const dir = Math.sign(cx[b2] - cx[a2]);
1214
+ els.push(arrow(id2, [[cx[a2], ay], [cx[b2] - dir * 2, ay]], common2));
1146
1215
  if (lbl)
1147
1216
  els.push(
1148
- text(`${id2}.t`, (cx[a] + cx[b]) / 2 - lm.width / 2, y, lbl, {
1217
+ text(`${id2}.t`, (cx[a2] + cx[b2]) / 2 - lm.width / 2, y, lbl, {
1149
1218
  fontSize: MSG_FONT,
1150
1219
  font,
1151
1220
  textAlign: "center",
@@ -1190,15 +1259,15 @@ function layoutSequence(spec, ctx) {
1190
1259
  });
1191
1260
  const headEls = [];
1192
1261
  P.forEach((p, i) => {
1193
- const b = boxes[i];
1262
+ const b2 = boxes[i];
1194
1263
  const id2 = `${ctx.idPrefix}.p.${p.id}`;
1195
1264
  const color = PALETTE[p.type === "actor" ? "pink" : p.type === "database" ? "cyan" : p.type === "external" ? "gray" : "blue"];
1196
- const s = shape(b.kind, id2, cx[i] - b.w / 2, (headH - b.h) / 2, b.w, b.h, {
1265
+ const s = shape(b2.kind, id2, cx[i] - b2.w / 2, (headH - b2.h) / 2, b2.w, b2.h, {
1197
1266
  backgroundColor: color[1],
1198
1267
  roughness,
1199
1268
  customData: ctx.cd("participant", p.id)
1200
1269
  });
1201
- headEls.push(s, boundText(s, b.lbl, { fontSize: 18, font, strokeColor: INK }));
1270
+ headEls.push(s, boundText(s, b2.lbl, { fontSize: 18, font, strokeColor: INK }));
1202
1271
  headEls.push(
1203
1272
  arrow(`${id2}.life`, [[cx[i], headH], [cx[i], bottom]], {
1204
1273
  kind: "line",
@@ -1215,7 +1284,7 @@ function layoutSequence(spec, ctx) {
1215
1284
 
1216
1285
  // ../diagram/src/render.ts
1217
1286
  var FRAME_PAD = 40;
1218
- var LAYOUT_VERSION = 1;
1287
+ var LAYOUT_VERSION = 2;
1219
1288
  var SpecError = class extends Error {
1220
1289
  constructor(issues) {
1221
1290
  super(`Invalid diagram spec:
@@ -1238,7 +1307,10 @@ async function renderDiagram(spec, opts) {
1238
1307
  const look = LOOKS[spec.style ?? "sketch"];
1239
1308
  const idPrefix = opts.diagramId;
1240
1309
  const cd = (role, ref) => vizoData(opts.diagramId, role, ref);
1241
- const ctx = { idPrefix, look, cd };
1310
+ const images = opts.images ?? {};
1311
+ const missing = specImageIds(spec).filter((id2) => !images[id2]);
1312
+ if (missing.length) throw new SpecError(missing.map((id2) => `image '${id2}' is not on this board; upload it with add_image and use the fileId it returns`));
1313
+ const ctx = { idPrefix, look, cd, images };
1242
1314
  let els;
1243
1315
  switch (spec.kind) {
1244
1316
  case "flowchart":
@@ -1260,11 +1332,11 @@ async function renderDiagram(spec, opts) {
1260
1332
  els = layoutMindmap(spec, ctx);
1261
1333
  break;
1262
1334
  }
1263
- const b = elementBounds(els);
1335
+ const b2 = elementBounds(els);
1264
1336
  const origin = opts.origin ?? { x: 0, y: 0 };
1265
- translate(els, origin.x + FRAME_PAD - b.minX, origin.y + FRAME_PAD - b.minY);
1266
- const w = b.maxX - b.minX + FRAME_PAD * 2;
1267
- const h = b.maxY - b.minY + FRAME_PAD * 2;
1337
+ translate(els, origin.x + FRAME_PAD - b2.minX, origin.y + FRAME_PAD - b2.minY);
1338
+ const w = b2.maxX - b2.minX + FRAME_PAD * 2;
1339
+ const h = b2.maxY - b2.minY + FRAME_PAD * 2;
1268
1340
  const frameId = `${opts.diagramId}.frame`;
1269
1341
  const f = frame(frameId, origin.x, origin.y, w, h, spec.title ?? defaultTitle(spec), {
1270
1342
  customData: cd("frame", opts.diagramId)
@@ -1307,105 +1379,2855 @@ var ConnectInput = z2.object({
1307
1379
  color: Color.optional()
1308
1380
  });
1309
1381
 
1310
- // src/index.ts
1311
- var PROXY_HEADER = "X-Vizo-Proxy";
1312
- var PUT_TOOL = "put_diagram_elements";
1313
- var LOCAL_TOOLS = /* @__PURE__ */ new Set(["draw_diagram", "update_diagram"]);
1314
- var toolError = (text2) => ({ content: [{ type: "text", text: text2 }], isError: true });
1315
- function arg(name) {
1316
- const i = process.argv.indexOf(`--${name}`);
1317
- if (i >= 0) return process.argv[i + 1];
1318
- const eq = process.argv.find((a) => a.startsWith(`--${name}=`));
1319
- return eq?.slice(name.length + 3);
1382
+ // src/export.ts
1383
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
1384
+ import { dirname, extname, resolve } from "node:path";
1385
+ import { fileURLToPath } from "node:url";
1386
+
1387
+ // ../../node_modules/.pnpm/hachure-fill@0.5.2/node_modules/hachure-fill/bin/hachure.js
1388
+ function rotatePoints(points, center, degrees) {
1389
+ if (points && points.length) {
1390
+ const [cx, cy] = center;
1391
+ const angle = Math.PI / 180 * degrees;
1392
+ const cos = Math.cos(angle);
1393
+ const sin = Math.sin(angle);
1394
+ for (const p of points) {
1395
+ const [x, y] = p;
1396
+ p[0] = (x - cx) * cos - (y - cy) * sin + cx;
1397
+ p[1] = (x - cx) * sin + (y - cy) * cos + cy;
1398
+ }
1399
+ }
1320
1400
  }
1321
- var log = (...a) => console.error("[vizo-mcp]", ...a);
1322
- async function main() {
1323
- const base2 = (arg("url") ?? process.env.VIZO_URL ?? "").replace(/\/$/, "");
1324
- const token = arg("token") ?? process.env.VIZO_TOKEN;
1325
- if (!base2 || !token) {
1326
- log("usage: vizo-mcp --url <https://your-vizo-host> --token <vz_\u2026> (or VIZO_URL / VIZO_TOKEN)");
1327
- log("Create a token at <url>/settings.");
1328
- process.exit(2);
1401
+ function rotateLines(lines, center, degrees) {
1402
+ const points = [];
1403
+ lines.forEach((line2) => points.push(...line2));
1404
+ rotatePoints(points, center, degrees);
1405
+ }
1406
+ function areSamePoints(p1, p2) {
1407
+ return p1[0] === p2[0] && p1[1] === p2[1];
1408
+ }
1409
+ function hachureLines(polygons, hachureGap, hachureAngle, hachureStepOffset = 1) {
1410
+ const angle = hachureAngle;
1411
+ const gap = Math.max(hachureGap, 0.1);
1412
+ const polygonList = polygons[0] && polygons[0][0] && typeof polygons[0][0] === "number" ? [polygons] : polygons;
1413
+ const rotationCenter = [0, 0];
1414
+ if (angle) {
1415
+ for (const polygon2 of polygonList) {
1416
+ rotatePoints(polygon2, rotationCenter, angle);
1417
+ }
1329
1418
  }
1330
- const endpoint = new URL(base2.endsWith("/mcp") ? base2 : `${base2}/mcp`);
1331
- const upstream = new Client({ name: "vizo-mcp-proxy", version: "0.1.0" });
1332
- await upstream.connect(
1333
- new StreamableHTTPClientTransport(endpoint, { requestInit: { headers: { Authorization: `Bearer ${token}`, [PROXY_HEADER]: String(LAYOUT_VERSION) } } })
1334
- );
1335
- const caps = upstream.getServerCapabilities() ?? {};
1336
- log(`connected to ${endpoint.href} (local layout v${LAYOUT_VERSION})`);
1337
- const server = new Server(
1338
- { name: "vizo", version: "0.1.0" },
1339
- {
1340
- capabilities: {
1341
- tools: caps.tools ? {} : void 0,
1342
- resources: caps.resources ? {} : void 0,
1343
- prompts: caps.prompts ? {} : void 0
1344
- },
1345
- instructions: upstream.getInstructions()
1419
+ const lines = straightHachureLines(polygonList, gap, hachureStepOffset);
1420
+ if (angle) {
1421
+ for (const polygon2 of polygonList) {
1422
+ rotatePoints(polygon2, rotationCenter, -angle);
1346
1423
  }
1347
- );
1348
- if (caps.tools) {
1349
- let serverSupportsLocal;
1350
- server.setRequestHandler(ListToolsRequestSchema, async (req) => {
1351
- const r = await upstream.listTools(req.params);
1352
- if (r.tools.some((t) => t.name === PUT_TOOL)) serverSupportsLocal = true;
1353
- return { ...r, tools: r.tools.filter((t) => t.name !== PUT_TOOL) };
1424
+ rotateLines(lines, rotationCenter, -angle);
1425
+ }
1426
+ return lines;
1427
+ }
1428
+ function straightHachureLines(polygons, gap, hachureStepOffset) {
1429
+ const vertexArray = [];
1430
+ for (const polygon2 of polygons) {
1431
+ const vertices = [...polygon2];
1432
+ if (!areSamePoints(vertices[0], vertices[vertices.length - 1])) {
1433
+ vertices.push([vertices[0][0], vertices[0][1]]);
1434
+ }
1435
+ if (vertices.length > 2) {
1436
+ vertexArray.push(vertices);
1437
+ }
1438
+ }
1439
+ const lines = [];
1440
+ gap = Math.max(gap, 0.1);
1441
+ const edges = [];
1442
+ for (const vertices of vertexArray) {
1443
+ for (let i = 0; i < vertices.length - 1; i++) {
1444
+ const p1 = vertices[i];
1445
+ const p2 = vertices[i + 1];
1446
+ if (p1[1] !== p2[1]) {
1447
+ const ymin = Math.min(p1[1], p2[1]);
1448
+ edges.push({
1449
+ ymin,
1450
+ ymax: Math.max(p1[1], p2[1]),
1451
+ x: ymin === p1[1] ? p1[0] : p2[0],
1452
+ islope: (p2[0] - p1[0]) / (p2[1] - p1[1])
1453
+ });
1454
+ }
1455
+ }
1456
+ }
1457
+ edges.sort((e1, e2) => {
1458
+ if (e1.ymin < e2.ymin) {
1459
+ return -1;
1460
+ }
1461
+ if (e1.ymin > e2.ymin) {
1462
+ return 1;
1463
+ }
1464
+ if (e1.x < e2.x) {
1465
+ return -1;
1466
+ }
1467
+ if (e1.x > e2.x) {
1468
+ return 1;
1469
+ }
1470
+ if (e1.ymax === e2.ymax) {
1471
+ return 0;
1472
+ }
1473
+ return (e1.ymax - e2.ymax) / Math.abs(e1.ymax - e2.ymax);
1474
+ });
1475
+ if (!edges.length) {
1476
+ return lines;
1477
+ }
1478
+ let activeEdges = [];
1479
+ let y = edges[0].ymin;
1480
+ let iteration = 0;
1481
+ while (activeEdges.length || edges.length) {
1482
+ if (edges.length) {
1483
+ let ix = -1;
1484
+ for (let i = 0; i < edges.length; i++) {
1485
+ if (edges[i].ymin > y) {
1486
+ break;
1487
+ }
1488
+ ix = i;
1489
+ }
1490
+ const removed = edges.splice(0, ix + 1);
1491
+ removed.forEach((edge) => {
1492
+ activeEdges.push({ s: y, edge });
1493
+ });
1494
+ }
1495
+ activeEdges = activeEdges.filter((ae2) => {
1496
+ if (ae2.edge.ymax <= y) {
1497
+ return false;
1498
+ }
1499
+ return true;
1354
1500
  });
1355
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
1356
- const { name, arguments: args = {} } = req.params;
1357
- if (!LOCAL_TOOLS.has(name)) return upstream.callTool(req.params);
1358
- serverSupportsLocal ??= (await upstream.listTools()).tools.some((t) => t.name === PUT_TOOL);
1359
- if (!serverSupportsLocal) {
1360
- return toolError(
1361
- `This Vizo server (${base2}) does not accept locally rendered diagrams (no ${PUT_TOOL}); it is older than this vizo-mcp (layout v${LAYOUT_VERSION}). Nothing was drawn. Update the server, or use a vizo-mcp version matching it.`
1362
- );
1501
+ activeEdges.sort((ae1, ae2) => {
1502
+ if (ae1.edge.x === ae2.edge.x) {
1503
+ return 0;
1363
1504
  }
1364
- return renderLocally(name, args);
1505
+ return (ae1.edge.x - ae2.edge.x) / Math.abs(ae1.edge.x - ae2.edge.x);
1506
+ });
1507
+ if (hachureStepOffset !== 1 || iteration % gap === 0) {
1508
+ if (activeEdges.length > 1) {
1509
+ for (let i = 0; i < activeEdges.length; i = i + 2) {
1510
+ const nexti = i + 1;
1511
+ if (nexti >= activeEdges.length) {
1512
+ break;
1513
+ }
1514
+ const ce2 = activeEdges[i].edge;
1515
+ const ne = activeEdges[nexti].edge;
1516
+ lines.push([
1517
+ [Math.round(ce2.x), y],
1518
+ [Math.round(ne.x), y]
1519
+ ]);
1520
+ }
1521
+ }
1522
+ }
1523
+ y += hachureStepOffset;
1524
+ activeEdges.forEach((ae2) => {
1525
+ ae2.edge.x = ae2.edge.x + hachureStepOffset * ae2.edge.islope;
1365
1526
  });
1527
+ iteration++;
1366
1528
  }
1367
- if (caps.resources) {
1368
- server.setRequestHandler(ListResourcesRequestSchema, (req) => upstream.listResources(req.params));
1369
- server.setRequestHandler(ListResourceTemplatesRequestSchema, (req) => upstream.listResourceTemplates(req.params));
1370
- server.setRequestHandler(ReadResourceRequestSchema, (req) => upstream.readResource(req.params));
1529
+ return lines;
1530
+ }
1531
+
1532
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/scan-line-hachure.js
1533
+ function polygonHachureLines(polygonList, o) {
1534
+ var _a;
1535
+ const angle = o.hachureAngle + 90;
1536
+ let gap = o.hachureGap;
1537
+ if (gap < 0) {
1538
+ gap = o.strokeWidth * 4;
1371
1539
  }
1372
- if (caps.prompts) {
1373
- server.setRequestHandler(ListPromptsRequestSchema, (req) => upstream.listPrompts(req.params));
1374
- server.setRequestHandler(GetPromptRequestSchema, (req) => upstream.getPrompt(req.params));
1540
+ gap = Math.round(Math.max(gap, 0.1));
1541
+ let skipOffset = 1;
1542
+ if (o.roughness >= 1) {
1543
+ if ((((_a = o.randomizer) === null || _a === void 0 ? void 0 : _a.next()) || Math.random()) > 0.7) {
1544
+ skipOffset = gap;
1545
+ }
1375
1546
  }
1376
- async function renderLocally(name, args) {
1377
- let spec;
1378
- try {
1379
- spec = parseSpec(args.spec);
1380
- } catch (e) {
1381
- if (e instanceof SpecError) return toolError(e.message);
1382
- throw e;
1547
+ return hachureLines(polygonList, gap, angle, skipOffset || 1);
1548
+ }
1549
+
1550
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/hachure-filler.js
1551
+ var HachureFiller = class {
1552
+ constructor(helper2) {
1553
+ this.helper = helper2;
1554
+ }
1555
+ fillPolygons(polygonList, o) {
1556
+ return this._fillPolygons(polygonList, o);
1557
+ }
1558
+ _fillPolygons(polygonList, o) {
1559
+ const lines = polygonHachureLines(polygonList, o);
1560
+ const ops = this.renderLines(lines, o);
1561
+ return { type: "fillSketch", ops };
1562
+ }
1563
+ renderLines(lines, o) {
1564
+ const ops = [];
1565
+ for (const line2 of lines) {
1566
+ ops.push(...this.helper.doubleLineOps(line2[0][0], line2[0][1], line2[1][0], line2[1][1], o));
1383
1567
  }
1384
- const drawing = name === "draw_diagram";
1385
- const diagramId = drawing ? `d-${randomId(8)}` : String(args.diagramId ?? "");
1386
- if (!diagramId) return toolError("diagramId is required");
1387
- const r = await renderDiagram(spec, { diagramId });
1388
- return upstream.callTool({
1389
- name: PUT_TOOL,
1390
- arguments: {
1391
- boardId: args.boardId,
1392
- mode: drawing ? "draw" : "update",
1393
- layoutVersion: LAYOUT_VERSION,
1394
- diagramId,
1395
- spec,
1396
- placement: args.placement,
1397
- elements: r.elements
1568
+ return ops;
1569
+ }
1570
+ };
1571
+
1572
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/geometry.js
1573
+ function lineLength(line2) {
1574
+ const p1 = line2[0];
1575
+ const p2 = line2[1];
1576
+ return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
1577
+ }
1578
+
1579
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/zigzag-filler.js
1580
+ var ZigZagFiller = class extends HachureFiller {
1581
+ fillPolygons(polygonList, o) {
1582
+ let gap = o.hachureGap;
1583
+ if (gap < 0) {
1584
+ gap = o.strokeWidth * 4;
1585
+ }
1586
+ gap = Math.max(gap, 0.1);
1587
+ const o2 = Object.assign({}, o, { hachureGap: gap });
1588
+ const lines = polygonHachureLines(polygonList, o2);
1589
+ const zigZagAngle = Math.PI / 180 * o.hachureAngle;
1590
+ const zigzagLines = [];
1591
+ const dgx = gap * 0.5 * Math.cos(zigZagAngle);
1592
+ const dgy = gap * 0.5 * Math.sin(zigZagAngle);
1593
+ for (const [p1, p2] of lines) {
1594
+ if (lineLength([p1, p2])) {
1595
+ zigzagLines.push([
1596
+ [p1[0] - dgx, p1[1] + dgy],
1597
+ [...p2]
1598
+ ], [
1599
+ [p1[0] + dgx, p1[1] - dgy],
1600
+ [...p2]
1601
+ ]);
1602
+ }
1603
+ }
1604
+ const ops = this.renderLines(zigzagLines, o);
1605
+ return { type: "fillSketch", ops };
1606
+ }
1607
+ };
1608
+
1609
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/hatch-filler.js
1610
+ var HatchFiller = class extends HachureFiller {
1611
+ fillPolygons(polygonList, o) {
1612
+ const set = this._fillPolygons(polygonList, o);
1613
+ const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 });
1614
+ const set2 = this._fillPolygons(polygonList, o2);
1615
+ set.ops = set.ops.concat(set2.ops);
1616
+ return set;
1617
+ }
1618
+ };
1619
+
1620
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/dot-filler.js
1621
+ var DotFiller = class {
1622
+ constructor(helper2) {
1623
+ this.helper = helper2;
1624
+ }
1625
+ fillPolygons(polygonList, o) {
1626
+ o = Object.assign({}, o, { hachureAngle: 0 });
1627
+ const lines = polygonHachureLines(polygonList, o);
1628
+ return this.dotsOnLines(lines, o);
1629
+ }
1630
+ dotsOnLines(lines, o) {
1631
+ const ops = [];
1632
+ let gap = o.hachureGap;
1633
+ if (gap < 0) {
1634
+ gap = o.strokeWidth * 4;
1635
+ }
1636
+ gap = Math.max(gap, 0.1);
1637
+ let fweight = o.fillWeight;
1638
+ if (fweight < 0) {
1639
+ fweight = o.strokeWidth / 2;
1640
+ }
1641
+ const ro = gap / 4;
1642
+ for (const line2 of lines) {
1643
+ const length = lineLength(line2);
1644
+ const dl = length / gap;
1645
+ const count = Math.ceil(dl) - 1;
1646
+ const offset = length - count * gap;
1647
+ const x = (line2[0][0] + line2[1][0]) / 2 - gap / 4;
1648
+ const minY = Math.min(line2[0][1], line2[1][1]);
1649
+ for (let i = 0; i < count; i++) {
1650
+ const y = minY + offset + i * gap;
1651
+ const cx = x - ro + Math.random() * 2 * ro;
1652
+ const cy = y - ro + Math.random() * 2 * ro;
1653
+ const el = this.helper.ellipse(cx, cy, fweight, fweight, o);
1654
+ ops.push(...el.ops);
1655
+ }
1656
+ }
1657
+ return { type: "fillSketch", ops };
1658
+ }
1659
+ };
1660
+
1661
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/dashed-filler.js
1662
+ var DashedFiller = class {
1663
+ constructor(helper2) {
1664
+ this.helper = helper2;
1665
+ }
1666
+ fillPolygons(polygonList, o) {
1667
+ const lines = polygonHachureLines(polygonList, o);
1668
+ return { type: "fillSketch", ops: this.dashedLine(lines, o) };
1669
+ }
1670
+ dashedLine(lines, o) {
1671
+ const offset = o.dashOffset < 0 ? o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap : o.dashOffset;
1672
+ const gap = o.dashGap < 0 ? o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap : o.dashGap;
1673
+ const ops = [];
1674
+ lines.forEach((line2) => {
1675
+ const length = lineLength(line2);
1676
+ const count = Math.floor(length / (offset + gap));
1677
+ const startOffset = (length + gap - count * (offset + gap)) / 2;
1678
+ let p1 = line2[0];
1679
+ let p2 = line2[1];
1680
+ if (p1[0] > p2[0]) {
1681
+ p1 = line2[1];
1682
+ p2 = line2[0];
1683
+ }
1684
+ const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0]));
1685
+ for (let i = 0; i < count; i++) {
1686
+ const lstart = i * (offset + gap);
1687
+ const lend = lstart + offset;
1688
+ const start = [p1[0] + lstart * Math.cos(alpha) + startOffset * Math.cos(alpha), p1[1] + lstart * Math.sin(alpha) + startOffset * Math.sin(alpha)];
1689
+ const end = [p1[0] + lend * Math.cos(alpha) + startOffset * Math.cos(alpha), p1[1] + lend * Math.sin(alpha) + startOffset * Math.sin(alpha)];
1690
+ ops.push(...this.helper.doubleLineOps(start[0], start[1], end[0], end[1], o));
1398
1691
  }
1399
1692
  });
1693
+ return ops;
1400
1694
  }
1401
- await server.connect(new StdioServerTransport());
1402
- const shutdown = async () => {
1403
- await Promise.allSettled([server.close(), upstream.close()]);
1404
- process.exit(0);
1405
- };
1406
- process.on("SIGINT", shutdown);
1407
- process.on("SIGTERM", shutdown);
1408
- process.stdin.on("close", shutdown);
1695
+ };
1696
+
1697
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/zigzag-line-filler.js
1698
+ var ZigZagLineFiller = class {
1699
+ constructor(helper2) {
1700
+ this.helper = helper2;
1701
+ }
1702
+ fillPolygons(polygonList, o) {
1703
+ const gap = o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap;
1704
+ const zo = o.zigzagOffset < 0 ? gap : o.zigzagOffset;
1705
+ o = Object.assign({}, o, { hachureGap: gap + zo });
1706
+ const lines = polygonHachureLines(polygonList, o);
1707
+ return { type: "fillSketch", ops: this.zigzagLines(lines, zo, o) };
1708
+ }
1709
+ zigzagLines(lines, zo, o) {
1710
+ const ops = [];
1711
+ lines.forEach((line2) => {
1712
+ const length = lineLength(line2);
1713
+ const count = Math.round(length / (2 * zo));
1714
+ let p1 = line2[0];
1715
+ let p2 = line2[1];
1716
+ if (p1[0] > p2[0]) {
1717
+ p1 = line2[1];
1718
+ p2 = line2[0];
1719
+ }
1720
+ const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0]));
1721
+ for (let i = 0; i < count; i++) {
1722
+ const lstart = i * 2 * zo;
1723
+ const lend = (i + 1) * 2 * zo;
1724
+ const dz = Math.sqrt(2 * Math.pow(zo, 2));
1725
+ const start = [p1[0] + lstart * Math.cos(alpha), p1[1] + lstart * Math.sin(alpha)];
1726
+ const end = [p1[0] + lend * Math.cos(alpha), p1[1] + lend * Math.sin(alpha)];
1727
+ const middle = [start[0] + dz * Math.cos(alpha + Math.PI / 4), start[1] + dz * Math.sin(alpha + Math.PI / 4)];
1728
+ ops.push(...this.helper.doubleLineOps(start[0], start[1], middle[0], middle[1], o), ...this.helper.doubleLineOps(middle[0], middle[1], end[0], end[1], o));
1729
+ }
1730
+ });
1731
+ return ops;
1732
+ }
1733
+ };
1734
+
1735
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/filler.js
1736
+ var fillers = {};
1737
+ function getFiller(o, helper2) {
1738
+ let fillerName = o.fillStyle || "hachure";
1739
+ if (!fillers[fillerName]) {
1740
+ switch (fillerName) {
1741
+ case "zigzag":
1742
+ if (!fillers[fillerName]) {
1743
+ fillers[fillerName] = new ZigZagFiller(helper2);
1744
+ }
1745
+ break;
1746
+ case "cross-hatch":
1747
+ if (!fillers[fillerName]) {
1748
+ fillers[fillerName] = new HatchFiller(helper2);
1749
+ }
1750
+ break;
1751
+ case "dots":
1752
+ if (!fillers[fillerName]) {
1753
+ fillers[fillerName] = new DotFiller(helper2);
1754
+ }
1755
+ break;
1756
+ case "dashed":
1757
+ if (!fillers[fillerName]) {
1758
+ fillers[fillerName] = new DashedFiller(helper2);
1759
+ }
1760
+ break;
1761
+ case "zigzag-line":
1762
+ if (!fillers[fillerName]) {
1763
+ fillers[fillerName] = new ZigZagLineFiller(helper2);
1764
+ }
1765
+ break;
1766
+ case "hachure":
1767
+ default:
1768
+ fillerName = "hachure";
1769
+ if (!fillers[fillerName]) {
1770
+ fillers[fillerName] = new HachureFiller(helper2);
1771
+ }
1772
+ break;
1773
+ }
1774
+ }
1775
+ return fillers[fillerName];
1776
+ }
1777
+
1778
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/math.js
1779
+ function randomSeed() {
1780
+ return Math.floor(Math.random() * 2 ** 31);
1781
+ }
1782
+ var Random = class {
1783
+ constructor(seed) {
1784
+ this.seed = seed;
1785
+ }
1786
+ next() {
1787
+ if (this.seed) {
1788
+ return (2 ** 31 - 1 & (this.seed = Math.imul(48271, this.seed))) / 2 ** 31;
1789
+ } else {
1790
+ return Math.random();
1791
+ }
1792
+ }
1793
+ };
1794
+
1795
+ // ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/parser.js
1796
+ var COMMAND = 0;
1797
+ var NUMBER = 1;
1798
+ var EOD = 2;
1799
+ var PARAMS = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 };
1800
+ function tokenize(d) {
1801
+ const tokens = new Array();
1802
+ while (d !== "") {
1803
+ if (d.match(/^([ \t\r\n,]+)/)) {
1804
+ d = d.substr(RegExp.$1.length);
1805
+ } else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) {
1806
+ tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 };
1807
+ d = d.substr(RegExp.$1.length);
1808
+ } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) {
1809
+ tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` };
1810
+ d = d.substr(RegExp.$1.length);
1811
+ } else {
1812
+ return [];
1813
+ }
1814
+ }
1815
+ tokens[tokens.length] = { type: EOD, text: "" };
1816
+ return tokens;
1817
+ }
1818
+ function isType(token, type) {
1819
+ return token.type === type;
1820
+ }
1821
+ function parsePath(d) {
1822
+ const segments = [];
1823
+ const tokens = tokenize(d);
1824
+ let mode = "BOD";
1825
+ let index = 0;
1826
+ let token = tokens[index];
1827
+ while (!isType(token, EOD)) {
1828
+ let paramsCount = 0;
1829
+ const params = [];
1830
+ if (mode === "BOD") {
1831
+ if (token.text === "M" || token.text === "m") {
1832
+ index++;
1833
+ paramsCount = PARAMS[token.text];
1834
+ mode = token.text;
1835
+ } else {
1836
+ return parsePath("M0,0" + d);
1837
+ }
1838
+ } else if (isType(token, NUMBER)) {
1839
+ paramsCount = PARAMS[mode];
1840
+ } else {
1841
+ index++;
1842
+ paramsCount = PARAMS[token.text];
1843
+ mode = token.text;
1844
+ }
1845
+ if (index + paramsCount < tokens.length) {
1846
+ for (let i = index; i < index + paramsCount; i++) {
1847
+ const numbeToken = tokens[i];
1848
+ if (isType(numbeToken, NUMBER)) {
1849
+ params[params.length] = +numbeToken.text;
1850
+ } else {
1851
+ throw new Error("Param not a number: " + mode + "," + numbeToken.text);
1852
+ }
1853
+ }
1854
+ if (typeof PARAMS[mode] === "number") {
1855
+ const segment = { key: mode, data: params };
1856
+ segments.push(segment);
1857
+ index += paramsCount;
1858
+ token = tokens[index];
1859
+ if (mode === "M")
1860
+ mode = "L";
1861
+ if (mode === "m")
1862
+ mode = "l";
1863
+ } else {
1864
+ throw new Error("Bad segment: " + mode);
1865
+ }
1866
+ } else {
1867
+ throw new Error("Path data ended short");
1868
+ }
1869
+ }
1870
+ return segments;
1871
+ }
1872
+
1873
+ // ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/absolutize.js
1874
+ function absolutize(segments) {
1875
+ let cx = 0, cy = 0;
1876
+ let subx = 0, suby = 0;
1877
+ const out = [];
1878
+ for (const { key, data } of segments) {
1879
+ switch (key) {
1880
+ case "M":
1881
+ out.push({ key: "M", data: [...data] });
1882
+ [cx, cy] = data;
1883
+ [subx, suby] = data;
1884
+ break;
1885
+ case "m":
1886
+ cx += data[0];
1887
+ cy += data[1];
1888
+ out.push({ key: "M", data: [cx, cy] });
1889
+ subx = cx;
1890
+ suby = cy;
1891
+ break;
1892
+ case "L":
1893
+ out.push({ key: "L", data: [...data] });
1894
+ [cx, cy] = data;
1895
+ break;
1896
+ case "l":
1897
+ cx += data[0];
1898
+ cy += data[1];
1899
+ out.push({ key: "L", data: [cx, cy] });
1900
+ break;
1901
+ case "C":
1902
+ out.push({ key: "C", data: [...data] });
1903
+ cx = data[4];
1904
+ cy = data[5];
1905
+ break;
1906
+ case "c": {
1907
+ const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
1908
+ out.push({ key: "C", data: newdata });
1909
+ cx = newdata[4];
1910
+ cy = newdata[5];
1911
+ break;
1912
+ }
1913
+ case "Q":
1914
+ out.push({ key: "Q", data: [...data] });
1915
+ cx = data[2];
1916
+ cy = data[3];
1917
+ break;
1918
+ case "q": {
1919
+ const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
1920
+ out.push({ key: "Q", data: newdata });
1921
+ cx = newdata[2];
1922
+ cy = newdata[3];
1923
+ break;
1924
+ }
1925
+ case "A":
1926
+ out.push({ key: "A", data: [...data] });
1927
+ cx = data[5];
1928
+ cy = data[6];
1929
+ break;
1930
+ case "a":
1931
+ cx += data[5];
1932
+ cy += data[6];
1933
+ out.push({ key: "A", data: [data[0], data[1], data[2], data[3], data[4], cx, cy] });
1934
+ break;
1935
+ case "H":
1936
+ out.push({ key: "H", data: [...data] });
1937
+ cx = data[0];
1938
+ break;
1939
+ case "h":
1940
+ cx += data[0];
1941
+ out.push({ key: "H", data: [cx] });
1942
+ break;
1943
+ case "V":
1944
+ out.push({ key: "V", data: [...data] });
1945
+ cy = data[0];
1946
+ break;
1947
+ case "v":
1948
+ cy += data[0];
1949
+ out.push({ key: "V", data: [cy] });
1950
+ break;
1951
+ case "S":
1952
+ out.push({ key: "S", data: [...data] });
1953
+ cx = data[2];
1954
+ cy = data[3];
1955
+ break;
1956
+ case "s": {
1957
+ const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
1958
+ out.push({ key: "S", data: newdata });
1959
+ cx = newdata[2];
1960
+ cy = newdata[3];
1961
+ break;
1962
+ }
1963
+ case "T":
1964
+ out.push({ key: "T", data: [...data] });
1965
+ cx = data[0];
1966
+ cy = data[1];
1967
+ break;
1968
+ case "t":
1969
+ cx += data[0];
1970
+ cy += data[1];
1971
+ out.push({ key: "T", data: [cx, cy] });
1972
+ break;
1973
+ case "Z":
1974
+ case "z":
1975
+ out.push({ key: "Z", data: [] });
1976
+ cx = subx;
1977
+ cy = suby;
1978
+ break;
1979
+ }
1980
+ }
1981
+ return out;
1982
+ }
1983
+
1984
+ // ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/normalize.js
1985
+ function normalize(segments) {
1986
+ const out = [];
1987
+ let lastType = "";
1988
+ let cx = 0, cy = 0;
1989
+ let subx = 0, suby = 0;
1990
+ let lcx = 0, lcy = 0;
1991
+ for (const { key, data } of segments) {
1992
+ switch (key) {
1993
+ case "M":
1994
+ out.push({ key: "M", data: [...data] });
1995
+ [cx, cy] = data;
1996
+ [subx, suby] = data;
1997
+ break;
1998
+ case "C":
1999
+ out.push({ key: "C", data: [...data] });
2000
+ cx = data[4];
2001
+ cy = data[5];
2002
+ lcx = data[2];
2003
+ lcy = data[3];
2004
+ break;
2005
+ case "L":
2006
+ out.push({ key: "L", data: [...data] });
2007
+ [cx, cy] = data;
2008
+ break;
2009
+ case "H":
2010
+ cx = data[0];
2011
+ out.push({ key: "L", data: [cx, cy] });
2012
+ break;
2013
+ case "V":
2014
+ cy = data[0];
2015
+ out.push({ key: "L", data: [cx, cy] });
2016
+ break;
2017
+ case "S": {
2018
+ let cx1 = 0, cy1 = 0;
2019
+ if (lastType === "C" || lastType === "S") {
2020
+ cx1 = cx + (cx - lcx);
2021
+ cy1 = cy + (cy - lcy);
2022
+ } else {
2023
+ cx1 = cx;
2024
+ cy1 = cy;
2025
+ }
2026
+ out.push({ key: "C", data: [cx1, cy1, ...data] });
2027
+ lcx = data[0];
2028
+ lcy = data[1];
2029
+ cx = data[2];
2030
+ cy = data[3];
2031
+ break;
2032
+ }
2033
+ case "T": {
2034
+ const [x, y] = data;
2035
+ let x1 = 0, y1 = 0;
2036
+ if (lastType === "Q" || lastType === "T") {
2037
+ x1 = cx + (cx - lcx);
2038
+ y1 = cy + (cy - lcy);
2039
+ } else {
2040
+ x1 = cx;
2041
+ y1 = cy;
2042
+ }
2043
+ const cx1 = cx + 2 * (x1 - cx) / 3;
2044
+ const cy1 = cy + 2 * (y1 - cy) / 3;
2045
+ const cx2 = x + 2 * (x1 - x) / 3;
2046
+ const cy2 = y + 2 * (y1 - y) / 3;
2047
+ out.push({ key: "C", data: [cx1, cy1, cx2, cy2, x, y] });
2048
+ lcx = x1;
2049
+ lcy = y1;
2050
+ cx = x;
2051
+ cy = y;
2052
+ break;
2053
+ }
2054
+ case "Q": {
2055
+ const [x1, y1, x, y] = data;
2056
+ const cx1 = cx + 2 * (x1 - cx) / 3;
2057
+ const cy1 = cy + 2 * (y1 - cy) / 3;
2058
+ const cx2 = x + 2 * (x1 - x) / 3;
2059
+ const cy2 = y + 2 * (y1 - y) / 3;
2060
+ out.push({ key: "C", data: [cx1, cy1, cx2, cy2, x, y] });
2061
+ lcx = x1;
2062
+ lcy = y1;
2063
+ cx = x;
2064
+ cy = y;
2065
+ break;
2066
+ }
2067
+ case "A": {
2068
+ const r1 = Math.abs(data[0]);
2069
+ const r22 = Math.abs(data[1]);
2070
+ const angle = data[2];
2071
+ const largeArcFlag = data[3];
2072
+ const sweepFlag = data[4];
2073
+ const x = data[5];
2074
+ const y = data[6];
2075
+ if (r1 === 0 || r22 === 0) {
2076
+ out.push({ key: "C", data: [cx, cy, x, y, x, y] });
2077
+ cx = x;
2078
+ cy = y;
2079
+ } else {
2080
+ if (cx !== x || cy !== y) {
2081
+ const curves = arcToCubicCurves(cx, cy, x, y, r1, r22, angle, largeArcFlag, sweepFlag);
2082
+ curves.forEach(function(curve2) {
2083
+ out.push({ key: "C", data: curve2 });
2084
+ });
2085
+ cx = x;
2086
+ cy = y;
2087
+ }
2088
+ }
2089
+ break;
2090
+ }
2091
+ case "Z":
2092
+ out.push({ key: "Z", data: [] });
2093
+ cx = subx;
2094
+ cy = suby;
2095
+ break;
2096
+ }
2097
+ lastType = key;
2098
+ }
2099
+ return out;
2100
+ }
2101
+ function degToRad(degrees) {
2102
+ return Math.PI * degrees / 180;
2103
+ }
2104
+ function rotate(x, y, angleRad) {
2105
+ const X = x * Math.cos(angleRad) - y * Math.sin(angleRad);
2106
+ const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);
2107
+ return [X, Y];
2108
+ }
2109
+ function arcToCubicCurves(x1, y1, x2, y2, r1, r22, angle, largeArcFlag, sweepFlag, recursive) {
2110
+ const angleRad = degToRad(angle);
2111
+ let params = [];
2112
+ let f1 = 0, f2 = 0, cx = 0, cy = 0;
2113
+ if (recursive) {
2114
+ [f1, f2, cx, cy] = recursive;
2115
+ } else {
2116
+ [x1, y1] = rotate(x1, y1, -angleRad);
2117
+ [x2, y2] = rotate(x2, y2, -angleRad);
2118
+ const x = (x1 - x2) / 2;
2119
+ const y = (y1 - y2) / 2;
2120
+ let h = x * x / (r1 * r1) + y * y / (r22 * r22);
2121
+ if (h > 1) {
2122
+ h = Math.sqrt(h);
2123
+ r1 = h * r1;
2124
+ r22 = h * r22;
2125
+ }
2126
+ const sign = largeArcFlag === sweepFlag ? -1 : 1;
2127
+ const r1Pow = r1 * r1;
2128
+ const r2Pow = r22 * r22;
2129
+ const left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x;
2130
+ const right = r1Pow * y * y + r2Pow * x * x;
2131
+ const k = sign * Math.sqrt(Math.abs(left / right));
2132
+ cx = k * r1 * y / r22 + (x1 + x2) / 2;
2133
+ cy = k * -r22 * x / r1 + (y1 + y2) / 2;
2134
+ f1 = Math.asin(parseFloat(((y1 - cy) / r22).toFixed(9)));
2135
+ f2 = Math.asin(parseFloat(((y2 - cy) / r22).toFixed(9)));
2136
+ if (x1 < cx) {
2137
+ f1 = Math.PI - f1;
2138
+ }
2139
+ if (x2 < cx) {
2140
+ f2 = Math.PI - f2;
2141
+ }
2142
+ if (f1 < 0) {
2143
+ f1 = Math.PI * 2 + f1;
2144
+ }
2145
+ if (f2 < 0) {
2146
+ f2 = Math.PI * 2 + f2;
2147
+ }
2148
+ if (sweepFlag && f1 > f2) {
2149
+ f1 = f1 - Math.PI * 2;
2150
+ }
2151
+ if (!sweepFlag && f2 > f1) {
2152
+ f2 = f2 - Math.PI * 2;
2153
+ }
2154
+ }
2155
+ let df = f2 - f1;
2156
+ if (Math.abs(df) > Math.PI * 120 / 180) {
2157
+ const f2old = f2;
2158
+ const x2old = x2;
2159
+ const y2old = y2;
2160
+ if (sweepFlag && f2 > f1) {
2161
+ f2 = f1 + Math.PI * 120 / 180 * 1;
2162
+ } else {
2163
+ f2 = f1 + Math.PI * 120 / 180 * -1;
2164
+ }
2165
+ x2 = cx + r1 * Math.cos(f2);
2166
+ y2 = cy + r22 * Math.sin(f2);
2167
+ params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r22, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
2168
+ }
2169
+ df = f2 - f1;
2170
+ const c1 = Math.cos(f1);
2171
+ const s1 = Math.sin(f1);
2172
+ const c2 = Math.cos(f2);
2173
+ const s2 = Math.sin(f2);
2174
+ const t = Math.tan(df / 4);
2175
+ const hx = 4 / 3 * r1 * t;
2176
+ const hy = 4 / 3 * r22 * t;
2177
+ const m1 = [x1, y1];
2178
+ const m2 = [x1 + hx * s1, y1 - hy * c1];
2179
+ const m3 = [x2 + hx * s2, y2 - hy * c2];
2180
+ const m4 = [x2, y2];
2181
+ m2[0] = 2 * m1[0] - m2[0];
2182
+ m2[1] = 2 * m1[1] - m2[1];
2183
+ if (recursive) {
2184
+ return [m2, m3, m4].concat(params);
2185
+ } else {
2186
+ params = [m2, m3, m4].concat(params);
2187
+ const curves = [];
2188
+ for (let i = 0; i < params.length; i += 3) {
2189
+ const r12 = rotate(params[i][0], params[i][1], angleRad);
2190
+ const r23 = rotate(params[i + 1][0], params[i + 1][1], angleRad);
2191
+ const r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad);
2192
+ curves.push([r12[0], r12[1], r23[0], r23[1], r3[0], r3[1]]);
2193
+ }
2194
+ return curves;
2195
+ }
2196
+ }
2197
+
2198
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/renderer.js
2199
+ var helper = {
2200
+ randOffset,
2201
+ randOffsetWithRange,
2202
+ ellipse,
2203
+ doubleLineOps: doubleLineFillOps
2204
+ };
2205
+ function line(x1, y1, x2, y2, o) {
2206
+ return { type: "path", ops: _doubleLine(x1, y1, x2, y2, o) };
2207
+ }
2208
+ function linearPath(points, close, o) {
2209
+ const len = (points || []).length;
2210
+ if (len > 2) {
2211
+ const ops = [];
2212
+ for (let i = 0; i < len - 1; i++) {
2213
+ ops.push(..._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o));
2214
+ }
2215
+ if (close) {
2216
+ ops.push(..._doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o));
2217
+ }
2218
+ return { type: "path", ops };
2219
+ } else if (len === 2) {
2220
+ return line(points[0][0], points[0][1], points[1][0], points[1][1], o);
2221
+ }
2222
+ return { type: "path", ops: [] };
2223
+ }
2224
+ function polygon(points, o) {
2225
+ return linearPath(points, true, o);
2226
+ }
2227
+ function rectangle(x, y, width, height, o) {
2228
+ const points = [
2229
+ [x, y],
2230
+ [x + width, y],
2231
+ [x + width, y + height],
2232
+ [x, y + height]
2233
+ ];
2234
+ return polygon(points, o);
2235
+ }
2236
+ function curve(inputPoints, o) {
2237
+ if (inputPoints.length) {
2238
+ const p1 = inputPoints[0];
2239
+ const pointsList = typeof p1[0] === "number" ? [inputPoints] : inputPoints;
2240
+ const o1 = _curveWithOffset(pointsList[0], 1 * (1 + o.roughness * 0.2), o);
2241
+ const o2 = o.disableMultiStroke ? [] : _curveWithOffset(pointsList[0], 1.5 * (1 + o.roughness * 0.22), cloneOptionsAlterSeed(o));
2242
+ for (let i = 1; i < pointsList.length; i++) {
2243
+ const points = pointsList[i];
2244
+ if (points.length) {
2245
+ const underlay = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o);
2246
+ const overlay = o.disableMultiStroke ? [] : _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), cloneOptionsAlterSeed(o));
2247
+ for (const item of underlay) {
2248
+ if (item.op !== "move") {
2249
+ o1.push(item);
2250
+ }
2251
+ }
2252
+ for (const item of overlay) {
2253
+ if (item.op !== "move") {
2254
+ o2.push(item);
2255
+ }
2256
+ }
2257
+ }
2258
+ }
2259
+ return { type: "path", ops: o1.concat(o2) };
2260
+ }
2261
+ return { type: "path", ops: [] };
2262
+ }
2263
+ function ellipse(x, y, width, height, o) {
2264
+ const params = generateEllipseParams(width, height, o);
2265
+ return ellipseWithParams(x, y, o, params).opset;
2266
+ }
2267
+ function generateEllipseParams(width, height, o) {
2268
+ const psq = Math.sqrt(Math.PI * 2 * Math.sqrt((Math.pow(width / 2, 2) + Math.pow(height / 2, 2)) / 2));
2269
+ const stepCount = Math.ceil(Math.max(o.curveStepCount, o.curveStepCount / Math.sqrt(200) * psq));
2270
+ const increment = Math.PI * 2 / stepCount;
2271
+ let rx = Math.abs(width / 2);
2272
+ let ry = Math.abs(height / 2);
2273
+ const curveFitRandomness = 1 - o.curveFitting;
2274
+ rx += _offsetOpt(rx * curveFitRandomness, o);
2275
+ ry += _offsetOpt(ry * curveFitRandomness, o);
2276
+ return { increment, rx, ry };
2277
+ }
2278
+ function ellipseWithParams(x, y, o, ellipseParams) {
2279
+ const [ap1, cp1] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1, ellipseParams.increment * _offset(0.1, _offset(0.4, 1, o), o), o);
2280
+ let o1 = _curve(ap1, null, o);
2281
+ if (!o.disableMultiStroke && o.roughness !== 0) {
2282
+ const [ap2] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1.5, 0, o);
2283
+ const o2 = _curve(ap2, null, o);
2284
+ o1 = o1.concat(o2);
2285
+ }
2286
+ return {
2287
+ estimatedPoints: cp1,
2288
+ opset: { type: "path", ops: o1 }
2289
+ };
2290
+ }
2291
+ function arc(x, y, width, height, start, stop, closed, roughClosure, o) {
2292
+ const cx = x;
2293
+ const cy = y;
2294
+ let rx = Math.abs(width / 2);
2295
+ let ry = Math.abs(height / 2);
2296
+ rx += _offsetOpt(rx * 0.01, o);
2297
+ ry += _offsetOpt(ry * 0.01, o);
2298
+ let strt = start;
2299
+ let stp = stop;
2300
+ while (strt < 0) {
2301
+ strt += Math.PI * 2;
2302
+ stp += Math.PI * 2;
2303
+ }
2304
+ if (stp - strt > Math.PI * 2) {
2305
+ strt = 0;
2306
+ stp = Math.PI * 2;
2307
+ }
2308
+ const ellipseInc = Math.PI * 2 / o.curveStepCount;
2309
+ const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2);
2310
+ const ops = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o);
2311
+ if (!o.disableMultiStroke) {
2312
+ const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o);
2313
+ ops.push(...o2);
2314
+ }
2315
+ if (closed) {
2316
+ if (roughClosure) {
2317
+ ops.push(..._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o), ..._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o));
2318
+ } else {
2319
+ ops.push({ op: "lineTo", data: [cx, cy] }, { op: "lineTo", data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] });
2320
+ }
2321
+ }
2322
+ return { type: "path", ops };
2323
+ }
2324
+ function svgPath(path, o) {
2325
+ const segments = normalize(absolutize(parsePath(path)));
2326
+ const ops = [];
2327
+ let first = [0, 0];
2328
+ let current = [0, 0];
2329
+ for (const { key, data } of segments) {
2330
+ switch (key) {
2331
+ case "M": {
2332
+ current = [data[0], data[1]];
2333
+ first = [data[0], data[1]];
2334
+ break;
2335
+ }
2336
+ case "L":
2337
+ ops.push(..._doubleLine(current[0], current[1], data[0], data[1], o));
2338
+ current = [data[0], data[1]];
2339
+ break;
2340
+ case "C": {
2341
+ const [x1, y1, x2, y2, x, y] = data;
2342
+ ops.push(..._bezierTo(x1, y1, x2, y2, x, y, current, o));
2343
+ current = [x, y];
2344
+ break;
2345
+ }
2346
+ case "Z":
2347
+ ops.push(..._doubleLine(current[0], current[1], first[0], first[1], o));
2348
+ current = [first[0], first[1]];
2349
+ break;
2350
+ }
2351
+ }
2352
+ return { type: "path", ops };
2353
+ }
2354
+ function solidFillPolygon(polygonList, o) {
2355
+ const ops = [];
2356
+ for (const points of polygonList) {
2357
+ if (points.length) {
2358
+ const offset = o.maxRandomnessOffset || 0;
2359
+ const len = points.length;
2360
+ if (len > 2) {
2361
+ ops.push({ op: "move", data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] });
2362
+ for (let i = 1; i < len; i++) {
2363
+ ops.push({ op: "lineTo", data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] });
2364
+ }
2365
+ }
2366
+ }
2367
+ }
2368
+ return { type: "fillPath", ops };
2369
+ }
2370
+ function patternFillPolygons(polygonList, o) {
2371
+ return getFiller(o, helper).fillPolygons(polygonList, o);
2372
+ }
2373
+ function patternFillArc(x, y, width, height, start, stop, o) {
2374
+ const cx = x;
2375
+ const cy = y;
2376
+ let rx = Math.abs(width / 2);
2377
+ let ry = Math.abs(height / 2);
2378
+ rx += _offsetOpt(rx * 0.01, o);
2379
+ ry += _offsetOpt(ry * 0.01, o);
2380
+ let strt = start;
2381
+ let stp = stop;
2382
+ while (strt < 0) {
2383
+ strt += Math.PI * 2;
2384
+ stp += Math.PI * 2;
2385
+ }
2386
+ if (stp - strt > Math.PI * 2) {
2387
+ strt = 0;
2388
+ stp = Math.PI * 2;
2389
+ }
2390
+ const increment = (stp - strt) / o.curveStepCount;
2391
+ const points = [];
2392
+ for (let angle = strt; angle <= stp; angle = angle + increment) {
2393
+ points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]);
2394
+ }
2395
+ points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]);
2396
+ points.push([cx, cy]);
2397
+ return patternFillPolygons([points], o);
2398
+ }
2399
+ function randOffset(x, o) {
2400
+ return _offsetOpt(x, o);
2401
+ }
2402
+ function randOffsetWithRange(min, max, o) {
2403
+ return _offset(min, max, o);
2404
+ }
2405
+ function doubleLineFillOps(x1, y1, x2, y2, o) {
2406
+ return _doubleLine(x1, y1, x2, y2, o, true);
2407
+ }
2408
+ function cloneOptionsAlterSeed(ops) {
2409
+ const result = Object.assign({}, ops);
2410
+ result.randomizer = void 0;
2411
+ if (ops.seed) {
2412
+ result.seed = ops.seed + 1;
2413
+ }
2414
+ return result;
2415
+ }
2416
+ function random(ops) {
2417
+ if (!ops.randomizer) {
2418
+ ops.randomizer = new Random(ops.seed || 0);
2419
+ }
2420
+ return ops.randomizer.next();
2421
+ }
2422
+ function _offset(min, max, ops, roughnessGain = 1) {
2423
+ return ops.roughness * roughnessGain * (random(ops) * (max - min) + min);
2424
+ }
2425
+ function _offsetOpt(x, ops, roughnessGain = 1) {
2426
+ return _offset(-x, x, ops, roughnessGain);
2427
+ }
2428
+ function _doubleLine(x1, y1, x2, y2, o, filling = false) {
2429
+ const singleStroke = filling ? o.disableMultiStrokeFill : o.disableMultiStroke;
2430
+ const o1 = _line(x1, y1, x2, y2, o, true, false);
2431
+ if (singleStroke) {
2432
+ return o1;
2433
+ }
2434
+ const o2 = _line(x1, y1, x2, y2, o, true, true);
2435
+ return o1.concat(o2);
2436
+ }
2437
+ function _line(x1, y1, x2, y2, o, move, overlay) {
2438
+ const lengthSq = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
2439
+ const length = Math.sqrt(lengthSq);
2440
+ let roughnessGain = 1;
2441
+ if (length < 200) {
2442
+ roughnessGain = 1;
2443
+ } else if (length > 500) {
2444
+ roughnessGain = 0.4;
2445
+ } else {
2446
+ roughnessGain = -16668e-7 * length + 1.233334;
2447
+ }
2448
+ let offset = o.maxRandomnessOffset || 0;
2449
+ if (offset * offset * 100 > lengthSq) {
2450
+ offset = length / 10;
2451
+ }
2452
+ const halfOffset = offset / 2;
2453
+ const divergePoint = 0.2 + random(o) * 0.2;
2454
+ let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200;
2455
+ let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200;
2456
+ midDispX = _offsetOpt(midDispX, o, roughnessGain);
2457
+ midDispY = _offsetOpt(midDispY, o, roughnessGain);
2458
+ const ops = [];
2459
+ const randomHalf = () => _offsetOpt(halfOffset, o, roughnessGain);
2460
+ const randomFull = () => _offsetOpt(offset, o, roughnessGain);
2461
+ const preserveVertices = o.preserveVertices;
2462
+ if (move) {
2463
+ if (overlay) {
2464
+ ops.push({
2465
+ op: "move",
2466
+ data: [
2467
+ x1 + (preserveVertices ? 0 : randomHalf()),
2468
+ y1 + (preserveVertices ? 0 : randomHalf())
2469
+ ]
2470
+ });
2471
+ } else {
2472
+ ops.push({
2473
+ op: "move",
2474
+ data: [
2475
+ x1 + (preserveVertices ? 0 : _offsetOpt(offset, o, roughnessGain)),
2476
+ y1 + (preserveVertices ? 0 : _offsetOpt(offset, o, roughnessGain))
2477
+ ]
2478
+ });
2479
+ }
2480
+ }
2481
+ if (overlay) {
2482
+ ops.push({
2483
+ op: "bcurveTo",
2484
+ data: [
2485
+ midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(),
2486
+ midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(),
2487
+ midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(),
2488
+ midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(),
2489
+ x2 + (preserveVertices ? 0 : randomHalf()),
2490
+ y2 + (preserveVertices ? 0 : randomHalf())
2491
+ ]
2492
+ });
2493
+ } else {
2494
+ ops.push({
2495
+ op: "bcurveTo",
2496
+ data: [
2497
+ midDispX + x1 + (x2 - x1) * divergePoint + randomFull(),
2498
+ midDispY + y1 + (y2 - y1) * divergePoint + randomFull(),
2499
+ midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(),
2500
+ midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(),
2501
+ x2 + (preserveVertices ? 0 : randomFull()),
2502
+ y2 + (preserveVertices ? 0 : randomFull())
2503
+ ]
2504
+ });
2505
+ }
2506
+ return ops;
2507
+ }
2508
+ function _curveWithOffset(points, offset, o) {
2509
+ if (!points.length) {
2510
+ return [];
2511
+ }
2512
+ const ps = [];
2513
+ ps.push([
2514
+ points[0][0] + _offsetOpt(offset, o),
2515
+ points[0][1] + _offsetOpt(offset, o)
2516
+ ]);
2517
+ ps.push([
2518
+ points[0][0] + _offsetOpt(offset, o),
2519
+ points[0][1] + _offsetOpt(offset, o)
2520
+ ]);
2521
+ for (let i = 1; i < points.length; i++) {
2522
+ ps.push([
2523
+ points[i][0] + _offsetOpt(offset, o),
2524
+ points[i][1] + _offsetOpt(offset, o)
2525
+ ]);
2526
+ if (i === points.length - 1) {
2527
+ ps.push([
2528
+ points[i][0] + _offsetOpt(offset, o),
2529
+ points[i][1] + _offsetOpt(offset, o)
2530
+ ]);
2531
+ }
2532
+ }
2533
+ return _curve(ps, null, o);
2534
+ }
2535
+ function _curve(points, closePoint, o) {
2536
+ const len = points.length;
2537
+ const ops = [];
2538
+ if (len > 3) {
2539
+ const b2 = [];
2540
+ const s = 1 - o.curveTightness;
2541
+ ops.push({ op: "move", data: [points[1][0], points[1][1]] });
2542
+ for (let i = 1; i + 2 < len; i++) {
2543
+ const cachedVertArray = points[i];
2544
+ b2[0] = [cachedVertArray[0], cachedVertArray[1]];
2545
+ b2[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6];
2546
+ b2[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6];
2547
+ b2[3] = [points[i + 1][0], points[i + 1][1]];
2548
+ ops.push({ op: "bcurveTo", data: [b2[1][0], b2[1][1], b2[2][0], b2[2][1], b2[3][0], b2[3][1]] });
2549
+ }
2550
+ if (closePoint && closePoint.length === 2) {
2551
+ const ro = o.maxRandomnessOffset;
2552
+ ops.push({ op: "lineTo", data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] });
2553
+ }
2554
+ } else if (len === 3) {
2555
+ ops.push({ op: "move", data: [points[1][0], points[1][1]] });
2556
+ ops.push({
2557
+ op: "bcurveTo",
2558
+ data: [
2559
+ points[1][0],
2560
+ points[1][1],
2561
+ points[2][0],
2562
+ points[2][1],
2563
+ points[2][0],
2564
+ points[2][1]
2565
+ ]
2566
+ });
2567
+ } else if (len === 2) {
2568
+ ops.push(..._line(points[0][0], points[0][1], points[1][0], points[1][1], o, true, true));
2569
+ }
2570
+ return ops;
2571
+ }
2572
+ function _computeEllipsePoints(increment, cx, cy, rx, ry, offset, overlap, o) {
2573
+ const coreOnly = o.roughness === 0;
2574
+ const corePoints = [];
2575
+ const allPoints = [];
2576
+ if (coreOnly) {
2577
+ increment = increment / 4;
2578
+ allPoints.push([
2579
+ cx + rx * Math.cos(-increment),
2580
+ cy + ry * Math.sin(-increment)
2581
+ ]);
2582
+ for (let angle = 0; angle <= Math.PI * 2; angle = angle + increment) {
2583
+ const p = [
2584
+ cx + rx * Math.cos(angle),
2585
+ cy + ry * Math.sin(angle)
2586
+ ];
2587
+ corePoints.push(p);
2588
+ allPoints.push(p);
2589
+ }
2590
+ allPoints.push([
2591
+ cx + rx * Math.cos(0),
2592
+ cy + ry * Math.sin(0)
2593
+ ]);
2594
+ allPoints.push([
2595
+ cx + rx * Math.cos(increment),
2596
+ cy + ry * Math.sin(increment)
2597
+ ]);
2598
+ } else {
2599
+ const radOffset = _offsetOpt(0.5, o) - Math.PI / 2;
2600
+ allPoints.push([
2601
+ _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
2602
+ _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
2603
+ ]);
2604
+ const endAngle = Math.PI * 2 + radOffset - 0.01;
2605
+ for (let angle = radOffset; angle < endAngle; angle = angle + increment) {
2606
+ const p = [
2607
+ _offsetOpt(offset, o) + cx + rx * Math.cos(angle),
2608
+ _offsetOpt(offset, o) + cy + ry * Math.sin(angle)
2609
+ ];
2610
+ corePoints.push(p);
2611
+ allPoints.push(p);
2612
+ }
2613
+ allPoints.push([
2614
+ _offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5),
2615
+ _offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5)
2616
+ ]);
2617
+ allPoints.push([
2618
+ _offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap),
2619
+ _offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap)
2620
+ ]);
2621
+ allPoints.push([
2622
+ _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5),
2623
+ _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5)
2624
+ ]);
2625
+ }
2626
+ return [allPoints, corePoints];
2627
+ }
2628
+ function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) {
2629
+ const radOffset = strt + _offsetOpt(0.1, o);
2630
+ const points = [];
2631
+ points.push([
2632
+ _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
2633
+ _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
2634
+ ]);
2635
+ for (let angle = radOffset; angle <= stp; angle = angle + increment) {
2636
+ points.push([
2637
+ _offsetOpt(offset, o) + cx + rx * Math.cos(angle),
2638
+ _offsetOpt(offset, o) + cy + ry * Math.sin(angle)
2639
+ ]);
2640
+ }
2641
+ points.push([
2642
+ cx + rx * Math.cos(stp),
2643
+ cy + ry * Math.sin(stp)
2644
+ ]);
2645
+ points.push([
2646
+ cx + rx * Math.cos(stp),
2647
+ cy + ry * Math.sin(stp)
2648
+ ]);
2649
+ return _curve(points, null, o);
2650
+ }
2651
+ function _bezierTo(x1, y1, x2, y2, x, y, current, o) {
2652
+ const ops = [];
2653
+ const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.3];
2654
+ let f = [0, 0];
2655
+ const iterations = o.disableMultiStroke ? 1 : 2;
2656
+ const preserveVertices = o.preserveVertices;
2657
+ for (let i = 0; i < iterations; i++) {
2658
+ if (i === 0) {
2659
+ ops.push({ op: "move", data: [current[0], current[1]] });
2660
+ } else {
2661
+ ops.push({ op: "move", data: [current[0] + (preserveVertices ? 0 : _offsetOpt(ros[0], o)), current[1] + (preserveVertices ? 0 : _offsetOpt(ros[0], o))] });
2662
+ }
2663
+ f = preserveVertices ? [x, y] : [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)];
2664
+ ops.push({
2665
+ op: "bcurveTo",
2666
+ data: [
2667
+ x1 + _offsetOpt(ros[i], o),
2668
+ y1 + _offsetOpt(ros[i], o),
2669
+ x2 + _offsetOpt(ros[i], o),
2670
+ y2 + _offsetOpt(ros[i], o),
2671
+ f[0],
2672
+ f[1]
2673
+ ]
2674
+ });
2675
+ }
2676
+ return ops;
2677
+ }
2678
+
2679
+ // ../../node_modules/.pnpm/points-on-curve@0.2.0/node_modules/points-on-curve/lib/curve-to-bezier.js
2680
+ function clone(p) {
2681
+ return [...p];
2682
+ }
2683
+ function curveToBezier(pointsIn, curveTightness = 0) {
2684
+ const len = pointsIn.length;
2685
+ if (len < 3) {
2686
+ throw new Error("A curve must have at least three points.");
2687
+ }
2688
+ const out = [];
2689
+ if (len === 3) {
2690
+ out.push(clone(pointsIn[0]), clone(pointsIn[1]), clone(pointsIn[2]), clone(pointsIn[2]));
2691
+ } else {
2692
+ const points = [];
2693
+ points.push(pointsIn[0], pointsIn[0]);
2694
+ for (let i = 1; i < pointsIn.length; i++) {
2695
+ points.push(pointsIn[i]);
2696
+ if (i === pointsIn.length - 1) {
2697
+ points.push(pointsIn[i]);
2698
+ }
2699
+ }
2700
+ const b2 = [];
2701
+ const s = 1 - curveTightness;
2702
+ out.push(clone(points[0]));
2703
+ for (let i = 1; i + 2 < points.length; i++) {
2704
+ const cachedVertArray = points[i];
2705
+ b2[0] = [cachedVertArray[0], cachedVertArray[1]];
2706
+ b2[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6];
2707
+ b2[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6];
2708
+ b2[3] = [points[i + 1][0], points[i + 1][1]];
2709
+ out.push(b2[1], b2[2], b2[3]);
2710
+ }
2711
+ }
2712
+ return out;
2713
+ }
2714
+
2715
+ // ../../node_modules/.pnpm/points-on-curve@0.2.0/node_modules/points-on-curve/lib/index.js
2716
+ function distance(p1, p2) {
2717
+ return Math.sqrt(distanceSq(p1, p2));
2718
+ }
2719
+ function distanceSq(p1, p2) {
2720
+ return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2);
2721
+ }
2722
+ function distanceToSegmentSq(p, v, w) {
2723
+ const l2 = distanceSq(v, w);
2724
+ if (l2 === 0) {
2725
+ return distanceSq(p, v);
2726
+ }
2727
+ let t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2;
2728
+ t = Math.max(0, Math.min(1, t));
2729
+ return distanceSq(p, lerp(v, w, t));
2730
+ }
2731
+ function lerp(a2, b2, t) {
2732
+ return [
2733
+ a2[0] + (b2[0] - a2[0]) * t,
2734
+ a2[1] + (b2[1] - a2[1]) * t
2735
+ ];
2736
+ }
2737
+ function flatness(points, offset) {
2738
+ const p1 = points[offset + 0];
2739
+ const p2 = points[offset + 1];
2740
+ const p3 = points[offset + 2];
2741
+ const p4 = points[offset + 3];
2742
+ let ux = 3 * p2[0] - 2 * p1[0] - p4[0];
2743
+ ux *= ux;
2744
+ let uy = 3 * p2[1] - 2 * p1[1] - p4[1];
2745
+ uy *= uy;
2746
+ let vx = 3 * p3[0] - 2 * p4[0] - p1[0];
2747
+ vx *= vx;
2748
+ let vy = 3 * p3[1] - 2 * p4[1] - p1[1];
2749
+ vy *= vy;
2750
+ if (ux < vx) {
2751
+ ux = vx;
2752
+ }
2753
+ if (uy < vy) {
2754
+ uy = vy;
2755
+ }
2756
+ return ux + uy;
2757
+ }
2758
+ function getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints) {
2759
+ const outPoints = newPoints || [];
2760
+ if (flatness(points, offset) < tolerance) {
2761
+ const p0 = points[offset + 0];
2762
+ if (outPoints.length) {
2763
+ const d = distance(outPoints[outPoints.length - 1], p0);
2764
+ if (d > 1) {
2765
+ outPoints.push(p0);
2766
+ }
2767
+ } else {
2768
+ outPoints.push(p0);
2769
+ }
2770
+ outPoints.push(points[offset + 3]);
2771
+ } else {
2772
+ const t = 0.5;
2773
+ const p1 = points[offset + 0];
2774
+ const p2 = points[offset + 1];
2775
+ const p3 = points[offset + 2];
2776
+ const p4 = points[offset + 3];
2777
+ const q1 = lerp(p1, p2, t);
2778
+ const q2 = lerp(p2, p3, t);
2779
+ const q3 = lerp(p3, p4, t);
2780
+ const r1 = lerp(q1, q2, t);
2781
+ const r22 = lerp(q2, q3, t);
2782
+ const red = lerp(r1, r22, t);
2783
+ getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints);
2784
+ getPointsOnBezierCurveWithSplitting([red, r22, q3, p4], 0, tolerance, outPoints);
2785
+ }
2786
+ return outPoints;
2787
+ }
2788
+ function simplify(points, distance2) {
2789
+ return simplifyPoints(points, 0, points.length, distance2);
2790
+ }
2791
+ function simplifyPoints(points, start, end, epsilon, newPoints) {
2792
+ const outPoints = newPoints || [];
2793
+ const s = points[start];
2794
+ const e = points[end - 1];
2795
+ let maxDistSq = 0;
2796
+ let maxNdx = 1;
2797
+ for (let i = start + 1; i < end - 1; ++i) {
2798
+ const distSq = distanceToSegmentSq(points[i], s, e);
2799
+ if (distSq > maxDistSq) {
2800
+ maxDistSq = distSq;
2801
+ maxNdx = i;
2802
+ }
2803
+ }
2804
+ if (Math.sqrt(maxDistSq) > epsilon) {
2805
+ simplifyPoints(points, start, maxNdx + 1, epsilon, outPoints);
2806
+ simplifyPoints(points, maxNdx, end, epsilon, outPoints);
2807
+ } else {
2808
+ if (!outPoints.length) {
2809
+ outPoints.push(s);
2810
+ }
2811
+ outPoints.push(e);
2812
+ }
2813
+ return outPoints;
2814
+ }
2815
+ function pointsOnBezierCurves(points, tolerance = 0.15, distance2) {
2816
+ const newPoints = [];
2817
+ const numSegments = (points.length - 1) / 3;
2818
+ for (let i = 0; i < numSegments; i++) {
2819
+ const offset = i * 3;
2820
+ getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints);
2821
+ }
2822
+ if (distance2 && distance2 > 0) {
2823
+ return simplifyPoints(newPoints, 0, newPoints.length, distance2);
2824
+ }
2825
+ return newPoints;
2826
+ }
2827
+
2828
+ // ../../node_modules/.pnpm/points-on-path@0.2.1/node_modules/points-on-path/lib/index.js
2829
+ function pointsOnPath(path, tolerance, distance2) {
2830
+ const segments = parsePath(path);
2831
+ const normalized = normalize(absolutize(segments));
2832
+ const sets = [];
2833
+ let currentPoints = [];
2834
+ let start = [0, 0];
2835
+ let pendingCurve = [];
2836
+ const appendPendingCurve = () => {
2837
+ if (pendingCurve.length >= 4) {
2838
+ currentPoints.push(...pointsOnBezierCurves(pendingCurve, tolerance));
2839
+ }
2840
+ pendingCurve = [];
2841
+ };
2842
+ const appendPendingPoints = () => {
2843
+ appendPendingCurve();
2844
+ if (currentPoints.length) {
2845
+ sets.push(currentPoints);
2846
+ currentPoints = [];
2847
+ }
2848
+ };
2849
+ for (const { key, data } of normalized) {
2850
+ switch (key) {
2851
+ case "M":
2852
+ appendPendingPoints();
2853
+ start = [data[0], data[1]];
2854
+ currentPoints.push(start);
2855
+ break;
2856
+ case "L":
2857
+ appendPendingCurve();
2858
+ currentPoints.push([data[0], data[1]]);
2859
+ break;
2860
+ case "C":
2861
+ if (!pendingCurve.length) {
2862
+ const lastPoint = currentPoints.length ? currentPoints[currentPoints.length - 1] : start;
2863
+ pendingCurve.push([lastPoint[0], lastPoint[1]]);
2864
+ }
2865
+ pendingCurve.push([data[0], data[1]]);
2866
+ pendingCurve.push([data[2], data[3]]);
2867
+ pendingCurve.push([data[4], data[5]]);
2868
+ break;
2869
+ case "Z":
2870
+ appendPendingCurve();
2871
+ currentPoints.push([start[0], start[1]]);
2872
+ break;
2873
+ }
2874
+ }
2875
+ appendPendingPoints();
2876
+ if (!distance2) {
2877
+ return sets;
2878
+ }
2879
+ const out = [];
2880
+ for (const set of sets) {
2881
+ const simplifiedSet = simplify(set, distance2);
2882
+ if (simplifiedSet.length) {
2883
+ out.push(simplifiedSet);
2884
+ }
2885
+ }
2886
+ return out;
2887
+ }
2888
+
2889
+ // ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/generator.js
2890
+ var NOS = "none";
2891
+ var RoughGenerator = class {
2892
+ constructor(config) {
2893
+ this.defaultOptions = {
2894
+ maxRandomnessOffset: 2,
2895
+ roughness: 1,
2896
+ bowing: 1,
2897
+ stroke: "#000",
2898
+ strokeWidth: 1,
2899
+ curveTightness: 0,
2900
+ curveFitting: 0.95,
2901
+ curveStepCount: 9,
2902
+ fillStyle: "hachure",
2903
+ fillWeight: -1,
2904
+ hachureAngle: -41,
2905
+ hachureGap: -1,
2906
+ dashOffset: -1,
2907
+ dashGap: -1,
2908
+ zigzagOffset: -1,
2909
+ seed: 0,
2910
+ disableMultiStroke: false,
2911
+ disableMultiStrokeFill: false,
2912
+ preserveVertices: false,
2913
+ fillShapeRoughnessGain: 0.8
2914
+ };
2915
+ this.config = config || {};
2916
+ if (this.config.options) {
2917
+ this.defaultOptions = this._o(this.config.options);
2918
+ }
2919
+ }
2920
+ static newSeed() {
2921
+ return randomSeed();
2922
+ }
2923
+ _o(options) {
2924
+ return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
2925
+ }
2926
+ _d(shape2, sets, options) {
2927
+ return { shape: shape2, sets: sets || [], options: options || this.defaultOptions };
2928
+ }
2929
+ line(x1, y1, x2, y2, options) {
2930
+ const o = this._o(options);
2931
+ return this._d("line", [line(x1, y1, x2, y2, o)], o);
2932
+ }
2933
+ rectangle(x, y, width, height, options) {
2934
+ const o = this._o(options);
2935
+ const paths = [];
2936
+ const outline = rectangle(x, y, width, height, o);
2937
+ if (o.fill) {
2938
+ const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]];
2939
+ if (o.fillStyle === "solid") {
2940
+ paths.push(solidFillPolygon([points], o));
2941
+ } else {
2942
+ paths.push(patternFillPolygons([points], o));
2943
+ }
2944
+ }
2945
+ if (o.stroke !== NOS) {
2946
+ paths.push(outline);
2947
+ }
2948
+ return this._d("rectangle", paths, o);
2949
+ }
2950
+ ellipse(x, y, width, height, options) {
2951
+ const o = this._o(options);
2952
+ const paths = [];
2953
+ const ellipseParams = generateEllipseParams(width, height, o);
2954
+ const ellipseResponse = ellipseWithParams(x, y, o, ellipseParams);
2955
+ if (o.fill) {
2956
+ if (o.fillStyle === "solid") {
2957
+ const shape2 = ellipseWithParams(x, y, o, ellipseParams).opset;
2958
+ shape2.type = "fillPath";
2959
+ paths.push(shape2);
2960
+ } else {
2961
+ paths.push(patternFillPolygons([ellipseResponse.estimatedPoints], o));
2962
+ }
2963
+ }
2964
+ if (o.stroke !== NOS) {
2965
+ paths.push(ellipseResponse.opset);
2966
+ }
2967
+ return this._d("ellipse", paths, o);
2968
+ }
2969
+ circle(x, y, diameter, options) {
2970
+ const ret = this.ellipse(x, y, diameter, diameter, options);
2971
+ ret.shape = "circle";
2972
+ return ret;
2973
+ }
2974
+ linearPath(points, options) {
2975
+ const o = this._o(options);
2976
+ return this._d("linearPath", [linearPath(points, false, o)], o);
2977
+ }
2978
+ arc(x, y, width, height, start, stop, closed = false, options) {
2979
+ const o = this._o(options);
2980
+ const paths = [];
2981
+ const outline = arc(x, y, width, height, start, stop, closed, true, o);
2982
+ if (closed && o.fill) {
2983
+ if (o.fillStyle === "solid") {
2984
+ const fillOptions = Object.assign({}, o);
2985
+ fillOptions.disableMultiStroke = true;
2986
+ const shape2 = arc(x, y, width, height, start, stop, true, false, fillOptions);
2987
+ shape2.type = "fillPath";
2988
+ paths.push(shape2);
2989
+ } else {
2990
+ paths.push(patternFillArc(x, y, width, height, start, stop, o));
2991
+ }
2992
+ }
2993
+ if (o.stroke !== NOS) {
2994
+ paths.push(outline);
2995
+ }
2996
+ return this._d("arc", paths, o);
2997
+ }
2998
+ curve(points, options) {
2999
+ const o = this._o(options);
3000
+ const paths = [];
3001
+ const outline = curve(points, o);
3002
+ if (o.fill && o.fill !== NOS) {
3003
+ if (o.fillStyle === "solid") {
3004
+ const fillShape = curve(points, Object.assign(Object.assign({}, o), { disableMultiStroke: true, roughness: o.roughness ? o.roughness + o.fillShapeRoughnessGain : 0 }));
3005
+ paths.push({
3006
+ type: "fillPath",
3007
+ ops: this._mergedShape(fillShape.ops)
3008
+ });
3009
+ } else {
3010
+ const polyPoints = [];
3011
+ const inputPoints = points;
3012
+ if (inputPoints.length) {
3013
+ const p1 = inputPoints[0];
3014
+ const pointsList = typeof p1[0] === "number" ? [inputPoints] : inputPoints;
3015
+ for (const points2 of pointsList) {
3016
+ if (points2.length < 3) {
3017
+ polyPoints.push(...points2);
3018
+ } else if (points2.length === 3) {
3019
+ polyPoints.push(...pointsOnBezierCurves(curveToBezier([
3020
+ points2[0],
3021
+ points2[0],
3022
+ points2[1],
3023
+ points2[2]
3024
+ ]), 10, (1 + o.roughness) / 2));
3025
+ } else {
3026
+ polyPoints.push(...pointsOnBezierCurves(curveToBezier(points2), 10, (1 + o.roughness) / 2));
3027
+ }
3028
+ }
3029
+ }
3030
+ if (polyPoints.length) {
3031
+ paths.push(patternFillPolygons([polyPoints], o));
3032
+ }
3033
+ }
3034
+ }
3035
+ if (o.stroke !== NOS) {
3036
+ paths.push(outline);
3037
+ }
3038
+ return this._d("curve", paths, o);
3039
+ }
3040
+ polygon(points, options) {
3041
+ const o = this._o(options);
3042
+ const paths = [];
3043
+ const outline = linearPath(points, true, o);
3044
+ if (o.fill) {
3045
+ if (o.fillStyle === "solid") {
3046
+ paths.push(solidFillPolygon([points], o));
3047
+ } else {
3048
+ paths.push(patternFillPolygons([points], o));
3049
+ }
3050
+ }
3051
+ if (o.stroke !== NOS) {
3052
+ paths.push(outline);
3053
+ }
3054
+ return this._d("polygon", paths, o);
3055
+ }
3056
+ path(d, options) {
3057
+ const o = this._o(options);
3058
+ const paths = [];
3059
+ if (!d) {
3060
+ return this._d("path", paths, o);
3061
+ }
3062
+ d = (d || "").replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(ss)/g", " ");
3063
+ const hasFill = o.fill && o.fill !== "transparent" && o.fill !== NOS;
3064
+ const hasStroke = o.stroke !== NOS;
3065
+ const simplified = !!(o.simplification && o.simplification < 1);
3066
+ const distance2 = simplified ? 4 - 4 * (o.simplification || 1) : (1 + o.roughness) / 2;
3067
+ const sets = pointsOnPath(d, 1, distance2);
3068
+ const shape2 = svgPath(d, o);
3069
+ if (hasFill) {
3070
+ if (o.fillStyle === "solid") {
3071
+ if (sets.length === 1) {
3072
+ const fillShape = svgPath(d, Object.assign(Object.assign({}, o), { disableMultiStroke: true, roughness: o.roughness ? o.roughness + o.fillShapeRoughnessGain : 0 }));
3073
+ paths.push({
3074
+ type: "fillPath",
3075
+ ops: this._mergedShape(fillShape.ops)
3076
+ });
3077
+ } else {
3078
+ paths.push(solidFillPolygon(sets, o));
3079
+ }
3080
+ } else {
3081
+ paths.push(patternFillPolygons(sets, o));
3082
+ }
3083
+ }
3084
+ if (hasStroke) {
3085
+ if (simplified) {
3086
+ sets.forEach((set) => {
3087
+ paths.push(linearPath(set, false, o));
3088
+ });
3089
+ } else {
3090
+ paths.push(shape2);
3091
+ }
3092
+ }
3093
+ return this._d("path", paths, o);
3094
+ }
3095
+ opsToPath(drawing, fixedDecimals) {
3096
+ let path = "";
3097
+ for (const item of drawing.ops) {
3098
+ const data = typeof fixedDecimals === "number" && fixedDecimals >= 0 ? item.data.map((d) => +d.toFixed(fixedDecimals)) : item.data;
3099
+ switch (item.op) {
3100
+ case "move":
3101
+ path += `M${data[0]} ${data[1]} `;
3102
+ break;
3103
+ case "bcurveTo":
3104
+ path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `;
3105
+ break;
3106
+ case "lineTo":
3107
+ path += `L${data[0]} ${data[1]} `;
3108
+ break;
3109
+ }
3110
+ }
3111
+ return path.trim();
3112
+ }
3113
+ toPaths(drawable) {
3114
+ const sets = drawable.sets || [];
3115
+ const o = drawable.options || this.defaultOptions;
3116
+ const paths = [];
3117
+ for (const drawing of sets) {
3118
+ let path = null;
3119
+ switch (drawing.type) {
3120
+ case "path":
3121
+ path = {
3122
+ d: this.opsToPath(drawing),
3123
+ stroke: o.stroke,
3124
+ strokeWidth: o.strokeWidth,
3125
+ fill: NOS
3126
+ };
3127
+ break;
3128
+ case "fillPath":
3129
+ path = {
3130
+ d: this.opsToPath(drawing),
3131
+ stroke: NOS,
3132
+ strokeWidth: 0,
3133
+ fill: o.fill || NOS
3134
+ };
3135
+ break;
3136
+ case "fillSketch":
3137
+ path = this.fillSketch(drawing, o);
3138
+ break;
3139
+ }
3140
+ if (path) {
3141
+ paths.push(path);
3142
+ }
3143
+ }
3144
+ return paths;
3145
+ }
3146
+ fillSketch(drawing, o) {
3147
+ let fweight = o.fillWeight;
3148
+ if (fweight < 0) {
3149
+ fweight = o.strokeWidth / 2;
3150
+ }
3151
+ return {
3152
+ d: this.opsToPath(drawing),
3153
+ stroke: o.fill || NOS,
3154
+ strokeWidth: fweight,
3155
+ fill: NOS
3156
+ };
3157
+ }
3158
+ _mergedShape(input) {
3159
+ return input.filter((d, i) => {
3160
+ if (i === 0) {
3161
+ return true;
3162
+ }
3163
+ if (d.op === "move") {
3164
+ return false;
3165
+ }
3166
+ return true;
3167
+ });
3168
+ }
3169
+ };
3170
+
3171
+ // ../../node_modules/.pnpm/perfect-freehand@1.2.0/node_modules/perfect-freehand/dist/esm/index.js
3172
+ function $(e, t, u, x = (h) => h) {
3173
+ return e * x(0.5 - t * (0.5 - u));
3174
+ }
3175
+ function se(e) {
3176
+ return [-e[0], -e[1]];
3177
+ }
3178
+ function l(e, t) {
3179
+ return [e[0] + t[0], e[1] + t[1]];
3180
+ }
3181
+ function a(e, t) {
3182
+ return [e[0] - t[0], e[1] - t[1]];
3183
+ }
3184
+ function b(e, t) {
3185
+ return [e[0] * t, e[1] * t];
3186
+ }
3187
+ function he(e, t) {
3188
+ return [e[0] / t, e[1] / t];
3189
+ }
3190
+ function R(e) {
3191
+ return [e[1], -e[0]];
3192
+ }
3193
+ function B(e, t) {
3194
+ return e[0] * t[0] + e[1] * t[1];
3195
+ }
3196
+ function ue(e, t) {
3197
+ return e[0] === t[0] && e[1] === t[1];
3198
+ }
3199
+ function ge(e) {
3200
+ return Math.hypot(e[0], e[1]);
3201
+ }
3202
+ function de(e) {
3203
+ return e[0] * e[0] + e[1] * e[1];
3204
+ }
3205
+ function A(e, t) {
3206
+ return de(a(e, t));
3207
+ }
3208
+ function G(e) {
3209
+ return he(e, ge(e));
3210
+ }
3211
+ function ie(e, t) {
3212
+ return Math.hypot(e[1] - t[1], e[0] - t[0]);
3213
+ }
3214
+ function L(e, t, u) {
3215
+ let x = Math.sin(u), h = Math.cos(u), y = e[0] - t[0], n = e[1] - t[1], f = y * h - n * x, d = y * x + n * h;
3216
+ return [f + t[0], d + t[1]];
3217
+ }
3218
+ function K(e, t, u) {
3219
+ return l(e, b(a(t, e), u));
3220
+ }
3221
+ function ee(e, t, u) {
3222
+ return l(e, b(t, u));
3223
+ }
3224
+ var { min: C, PI: xe } = Math;
3225
+ var pe = 0.275;
3226
+ var V = xe + 1e-4;
3227
+ function ce(e, t = {}) {
3228
+ let { size: u = 16, smoothing: x = 0.5, thinning: h = 0.5, simulatePressure: y = true, easing: n = (r) => r, start: f = {}, end: d = {}, last: D = false } = t, { cap: S = true, easing: j = (r) => r * (2 - r) } = f, { cap: q = true, easing: c = (r) => --r * r * r + 1 } = d;
3229
+ if (e.length === 0 || u <= 0) return [];
3230
+ let p = e[e.length - 1].runningLength, g = f.taper === false ? 0 : f.taper === true ? Math.max(u, p) : f.taper, T = d.taper === false ? 0 : d.taper === true ? Math.max(u, p) : d.taper, te = Math.pow(u * x, 2), _ = [], M = [], H = e.slice(0, 10).reduce((r, i) => {
3231
+ let o = i.pressure;
3232
+ if (y) {
3233
+ let s = C(1, i.distance / u), W2 = C(1, 1 - s);
3234
+ o = C(1, r + (W2 - r) * (s * pe));
3235
+ }
3236
+ return (r + o) / 2;
3237
+ }, e[0].pressure), m = $(u, h, e[e.length - 1].pressure, n), U, X = e[0].vector, z3 = e[0].point, F = z3, O = z3, E = F, J = false;
3238
+ for (let r = 0; r < e.length; r++) {
3239
+ let { pressure: i } = e[r], { point: o, vector: s, distance: W2, runningLength: I } = e[r];
3240
+ if (r < e.length - 1 && p - I < 3) continue;
3241
+ if (h) {
3242
+ if (y) {
3243
+ let v = C(1, W2 / u), Z = C(1, 1 - v);
3244
+ i = C(1, H + (Z - H) * (v * pe));
3245
+ }
3246
+ m = $(u, h, i, n);
3247
+ } else m = u / 2;
3248
+ U === void 0 && (U = m);
3249
+ let le = I < g ? j(I / g) : 1, fe = p - I < T ? c((p - I) / T) : 1;
3250
+ m = Math.max(0.01, m * Math.min(le, fe));
3251
+ let re = (r < e.length - 1 ? e[r + 1] : e[r]).vector, Y = r < e.length - 1 ? B(s, re) : 1, be = B(s, X) < 0 && !J, ne = Y !== null && Y < 0;
3252
+ if (be || ne) {
3253
+ let v = b(R(X), m);
3254
+ for (let Z = 1 / 13, w = 0; w <= 1; w += Z) O = L(a(o, v), o, V * w), _.push(O), E = L(l(o, v), o, V * -w), M.push(E);
3255
+ z3 = O, F = E, ne && (J = true);
3256
+ continue;
3257
+ }
3258
+ if (J = false, r === e.length - 1) {
3259
+ let v = b(R(s), m);
3260
+ _.push(a(o, v)), M.push(l(o, v));
3261
+ continue;
3262
+ }
3263
+ let oe = b(R(K(re, s, Y)), m);
3264
+ O = a(o, oe), (r <= 1 || A(z3, O) > te) && (_.push(O), z3 = O), E = l(o, oe), (r <= 1 || A(F, E) > te) && (M.push(E), F = E), H = i, X = s;
3265
+ }
3266
+ let P = e[0].point.slice(0, 2), k = e.length > 1 ? e[e.length - 1].point.slice(0, 2) : l(e[0].point, [1, 1]), Q = [], N = [];
3267
+ if (e.length === 1) {
3268
+ if (!(g || T) || D) {
3269
+ let r = ee(P, G(R(a(P, k))), -(U || m)), i = [];
3270
+ for (let o = 1 / 13, s = o; s <= 1; s += o) i.push(L(r, P, V * 2 * s));
3271
+ return i;
3272
+ }
3273
+ } else {
3274
+ if (!(g || T && e.length === 1)) if (S) for (let i = 1 / 13, o = i; o <= 1; o += i) {
3275
+ let s = L(M[0], P, V * o);
3276
+ Q.push(s);
3277
+ }
3278
+ else {
3279
+ let i = a(_[0], M[0]), o = b(i, 0.5), s = b(i, 0.51);
3280
+ Q.push(a(P, o), a(P, s), l(P, s), l(P, o));
3281
+ }
3282
+ let r = R(se(e[e.length - 1].vector));
3283
+ if (T || g && e.length === 1) N.push(k);
3284
+ else if (q) {
3285
+ let i = ee(k, r, m);
3286
+ for (let o = 1 / 29, s = o; s < 1; s += o) N.push(L(i, k, V * 3 * s));
3287
+ } else N.push(l(k, b(r, m)), l(k, b(r, m * 0.99)), a(k, b(r, m * 0.99)), a(k, b(r, m)));
3288
+ }
3289
+ return _.concat(N, M.reverse(), Q);
3290
+ }
3291
+ function me(e, t = {}) {
3292
+ var q;
3293
+ let { streamline: u = 0.5, size: x = 16, last: h = false } = t;
3294
+ if (e.length === 0) return [];
3295
+ let y = 0.15 + (1 - u) * 0.85, n = Array.isArray(e[0]) ? e : e.map(({ x: c, y: p, pressure: g = 0.5 }) => [c, p, g]);
3296
+ if (n.length === 2) {
3297
+ let c = n[1];
3298
+ n = n.slice(0, -1);
3299
+ for (let p = 1; p < 5; p++) n.push(K(n[0], c, p / 4));
3300
+ }
3301
+ n.length === 1 && (n = [...n, [...l(n[0], [1, 1]), ...n[0].slice(2)]]);
3302
+ let f = [{ point: [n[0][0], n[0][1]], pressure: n[0][2] >= 0 ? n[0][2] : 0.25, vector: [1, 1], distance: 0, runningLength: 0 }], d = false, D = 0, S = f[0], j = n.length - 1;
3303
+ for (let c = 1; c < n.length; c++) {
3304
+ let p = h && c === j ? n[c].slice(0, 2) : K(S.point, n[c], y);
3305
+ if (ue(S.point, p)) continue;
3306
+ let g = ie(p, S.point);
3307
+ if (D += g, c < j && !d) {
3308
+ if (D < x) continue;
3309
+ d = true;
3310
+ }
3311
+ S = { point: p, pressure: n[c][2] >= 0 ? n[c][2] : 0.5, vector: G(a(S.point, p)), distance: g, runningLength: D }, f.push(S);
3312
+ }
3313
+ return f[0].vector = ((q = f[1]) == null ? void 0 : q.vector) || [0, 0], f;
3314
+ }
3315
+ function ae(e, t = {}) {
3316
+ return ce(me(e, t), t);
3317
+ }
3318
+
3319
+ // ../diagram/src/export/svg.ts
3320
+ var FONT_FAMILY = {
3321
+ 1: "Virgil",
3322
+ 2: "Helvetica",
3323
+ 3: "Cascadia",
3324
+ 5: "Excalifont",
3325
+ 6: "Nunito",
3326
+ 7: "Lilita One",
3327
+ 8: "Comic Shanns",
3328
+ 9: "Liberation Sans"
3329
+ };
3330
+ var METRICS = {
3331
+ 1: { unitsPerEm: 1e3, ascender: 886, descender: -374 },
3332
+ 2: { unitsPerEm: 2048, ascender: 1577, descender: -471 },
3333
+ 3: { unitsPerEm: 2048, ascender: 1900, descender: -480 },
3334
+ 5: { unitsPerEm: 1e3, ascender: 886, descender: -374 },
3335
+ 6: { unitsPerEm: 1e3, ascender: 1011, descender: -353 },
3336
+ 7: { unitsPerEm: 1e3, ascender: 923, descender: -220 },
3337
+ 8: { unitsPerEm: 1e3, ascender: 750, descender: -250 },
3338
+ 9: { unitsPerEm: 2048, ascender: 1854, descender: -434 }
3339
+ };
3340
+ var fontFamilyCss = (id2) => {
3341
+ const name = FONT_FAMILY[id2];
3342
+ if (!name) return "Segoe UI Emoji";
3343
+ return id2 === 5 ? `${name}, Xiaolai, Segoe UI Emoji` : `${name}, Segoe UI Emoji`;
3344
+ };
3345
+ function fontsUsed(elements) {
3346
+ const s = /* @__PURE__ */ new Set();
3347
+ for (const e of elements) {
3348
+ if (e.type === "text") s.add(e.fontFamily ?? 5);
3349
+ if (e.type === "frame" || e.type === "magicframe") s.add(2);
3350
+ }
3351
+ return s;
3352
+ }
3353
+ var DECIMALS = 2;
3354
+ var r2 = (n) => Math.round(n * 100) / 100;
3355
+ var rotate2 = ([x, y], [cx, cy], a2) => [
3356
+ (x - cx) * Math.cos(a2) - (y - cy) * Math.sin(a2) + cx,
3357
+ (x - cx) * Math.sin(a2) + (y - cy) * Math.cos(a2) + cy
3358
+ ];
3359
+ var isLinear = (e) => e.type === "arrow" || e.type === "line";
3360
+ var isFrame = (e) => e.type === "frame" || e.type === "magicframe";
3361
+ var isTransparent = (c) => !c || c === "transparent" || c.length === 5 && c.endsWith("0") || c.length === 9 && c.endsWith("00");
3362
+ function localBounds(e) {
3363
+ if ((isLinear(e) || e.type === "freedraw") && e.points?.length) {
3364
+ const xs = e.points.map((p) => p[0]);
3365
+ const ys = e.points.map((p) => p[1]);
3366
+ return [Math.min(...xs), Math.min(...ys), Math.max(...xs), Math.max(...ys)];
3367
+ }
3368
+ return [0, 0, e.width, e.height];
3369
+ }
3370
+ function elementBounds2(e) {
3371
+ const [x1, y1, x2, y2] = localBounds(e);
3372
+ const c = [e.x + (x1 + x2) / 2, e.y + (y1 + y2) / 2];
3373
+ const pts = (isLinear(e) || e.type === "freedraw") && e.points?.length ? e.points.map((p) => [e.x + p[0], e.y + p[1]]) : [
3374
+ [e.x + x1, e.y + y1],
3375
+ [e.x + x2, e.y + y1],
3376
+ [e.x + x2, e.y + y2],
3377
+ [e.x + x1, e.y + y2]
3378
+ ];
3379
+ const r = e.angle ? pts.map((p) => rotate2(p, c, e.angle)) : pts;
3380
+ return [Math.min(...r.map((p) => p[0])), Math.min(...r.map((p) => p[1])), Math.max(...r.map((p) => p[0])), Math.max(...r.map((p) => p[1]))];
3381
+ }
3382
+ var gen = new RoughGenerator();
3383
+ var DEFAULT_PROPORTIONAL_RADIUS = 0.25;
3384
+ var DEFAULT_ADAPTIVE_RADIUS = 32;
3385
+ function cornerRadius(x, e) {
3386
+ const t = e.roundness?.type;
3387
+ if (t === 2 || t === 1) return x * DEFAULT_PROPORTIONAL_RADIUS;
3388
+ if (t === 3) {
3389
+ const fixed = e.roundness?.value ?? DEFAULT_ADAPTIVE_RADIUS;
3390
+ return x <= fixed / DEFAULT_PROPORTIONAL_RADIUS ? x * DEFAULT_PROPORTIONAL_RADIUS : fixed;
3391
+ }
3392
+ return 0;
3393
+ }
3394
+ var isPathALoop = (points) => points.length >= 3 && Math.hypot(points[0][0] - points.at(-1)[0], points[0][1] - points.at(-1)[1]) <= 8;
3395
+ function adjustRoughness(e) {
3396
+ const max = Math.max(e.width, e.height);
3397
+ const min = Math.min(e.width, e.height);
3398
+ const roundable = e.type === "rectangle" || e.type === "diamond" || isLinear(e);
3399
+ if (min >= 20 && max >= 50 || min >= 15 && !!e.roundness && roundable || isLinear(e) && max >= 50) return e.roughness;
3400
+ return Math.min(e.roughness / (max < 10 ? 3 : 2), 2.5);
3401
+ }
3402
+ function roughOptions(e, continuousPath = false) {
3403
+ const sw = e.strokeWidth ?? 2;
3404
+ const o = {
3405
+ seed: e.seed,
3406
+ strokeLineDash: e.strokeStyle === "dashed" ? [8, 8 + sw] : e.strokeStyle === "dotted" ? [1.5, 6 + sw] : void 0,
3407
+ disableMultiStroke: e.strokeStyle !== "solid",
3408
+ strokeWidth: e.strokeStyle !== "solid" ? sw + 0.5 : sw,
3409
+ fillWeight: sw / 2,
3410
+ hachureGap: sw * 4,
3411
+ roughness: adjustRoughness(e),
3412
+ stroke: e.strokeColor,
3413
+ preserveVertices: continuousPath || e.roughness < 2,
3414
+ fixedDecimalPlaceDigits: DECIMALS
3415
+ };
3416
+ if (e.type === "rectangle" || e.type === "diamond" || e.type === "ellipse") {
3417
+ o.fillStyle = e.fillStyle;
3418
+ o.fill = isTransparent(e.backgroundColor) ? void 0 : e.backgroundColor;
3419
+ if (e.type === "ellipse") o.curveFitting = 1;
3420
+ } else if ((e.type === "line" || e.type === "freedraw") && isPathALoop(e.points ?? [])) {
3421
+ o.fillStyle = e.fillStyle;
3422
+ o.fill = e.backgroundColor === "transparent" ? void 0 : e.backgroundColor;
3423
+ }
3424
+ return o;
3425
+ }
3426
+ function diamondPoints(e) {
3427
+ const topX = Math.floor(e.width / 2) + 1;
3428
+ const rightY = Math.floor(e.height / 2) + 1;
3429
+ return [topX, 0, e.width, rightY, topX, e.height, 0, rightY];
3430
+ }
3431
+ function elbowPath(points, radius) {
3432
+ const sub = [];
3433
+ const horizontal = (a2, b2) => Math.abs(a2[0] - b2[0]) >= Math.abs(a2[1] - b2[1]);
3434
+ for (let i = 1; i < points.length - 1; i++) {
3435
+ const [prev, p, next] = [points[i - 1], points[i], points[i + 1]];
3436
+ const corner = Math.min(radius, Math.hypot(p[0] - next[0], p[1] - next[1]) / 2, Math.hypot(p[0] - prev[0], p[1] - prev[1]) / 2);
3437
+ if (horizontal(p, prev)) sub.push(prev[0] < p[0] ? [p[0] - corner, p[1]] : [p[0] + corner, p[1]]);
3438
+ else sub.push(prev[1] < p[1] ? [p[0], p[1] - corner] : [p[0], p[1] + corner]);
3439
+ sub.push(p);
3440
+ if (horizontal(next, p)) sub.push(next[0] < p[0] ? [p[0] - corner, p[1]] : [p[0] + corner, p[1]]);
3441
+ else sub.push(next[1] < p[1] ? [p[0], p[1] - corner] : [p[0], p[1] + corner]);
3442
+ }
3443
+ const d = [`M ${points[0][0]} ${points[0][1]}`];
3444
+ for (let i = 0; i < sub.length; i += 3) d.push(`L ${sub[i][0]} ${sub[i][1]}`, `Q ${sub[i + 1][0]} ${sub[i + 1][1]}, ${sub[i + 2][0]} ${sub[i + 2][1]}`);
3445
+ d.push(`L ${points.at(-1)[0]} ${points.at(-1)[1]}`);
3446
+ return d.join(" ");
3447
+ }
3448
+ function arrowheadSize(a2) {
3449
+ if (a2 === "arrow") return 25;
3450
+ if (a2 === "diamond" || a2 === "diamond_outline") return 12;
3451
+ if (a2.startsWith("crowfoot")) return 20;
3452
+ return 15;
3453
+ }
3454
+ var arrowheadAngle = (a2) => a2 === "bar" ? 90 : a2 === "arrow" ? 20 : 25;
3455
+ function curveOps(shape2) {
3456
+ return (shape2.sets.find((s) => s.type === "path") ?? shape2.sets[0]).ops;
3457
+ }
3458
+ function arrowheadPoints(e, shape2, position, head) {
3459
+ const ops = curveOps(shape2);
3460
+ if (ops.length < 2) return null;
3461
+ const index = position === "start" ? 1 : ops.length - 1;
3462
+ const data = ops[index].data;
3463
+ if (data.length !== 6) return null;
3464
+ const p3 = [data[4], data[5]];
3465
+ const p2 = [data[2], data[3]];
3466
+ const p1 = [data[0], data[1]];
3467
+ const prev = ops[index - 1];
3468
+ let p0 = [0, 0];
3469
+ if (prev.op === "move") p0 = [prev.data[0], prev.data[1]];
3470
+ else if (prev.op === "bcurveTo") p0 = [prev.data[4], prev.data[5]];
3471
+ const eq = (t, i) => (1 - t) ** 3 * p3[i] + 3 * t * (1 - t) ** 2 * p2[i] + 3 * t ** 2 * (1 - t) * p1[i] + p0[i] * t ** 3;
3472
+ const [x2, y2] = position === "start" ? p0 : p3;
3473
+ const [x1, y1] = [eq(0.3, 0), eq(0.3, 1)];
3474
+ const dist = Math.hypot(x2 - x1, y2 - y1);
3475
+ const nx = (x2 - x1) / dist;
3476
+ const ny = (y2 - y1) / dist;
3477
+ const pts = e.points;
3478
+ const [cx, cy] = position === "end" ? pts.at(-1) : pts[0];
3479
+ const [px, py] = pts.length > 1 ? position === "end" ? pts.at(-2) : pts[1] : [0, 0];
3480
+ const length = Math.hypot(cx - px, cy - py);
3481
+ const minSize = Math.min(arrowheadSize(head), length * (head === "diamond" || head === "diamond_outline" ? 0.25 : 0.5));
3482
+ const xs = x2 - nx * minSize;
3483
+ const ys = y2 - ny * minSize;
3484
+ if (head === "dot" || head === "circle" || head === "circle_outline") return [x2, y2, Math.hypot(ys - y2, xs - x2) + (e.strokeWidth ?? 2) - 2];
3485
+ const angle = arrowheadAngle(head) * Math.PI / 180;
3486
+ if (head === "crowfoot_many" || head === "crowfoot_one_or_many") {
3487
+ const [x32, y32] = rotate2([x2, y2], [xs, ys], -angle);
3488
+ const [x42, y42] = rotate2([x2, y2], [xs, ys], angle);
3489
+ return [xs, ys, x32, y32, x42, y42];
3490
+ }
3491
+ const [x3, y3] = rotate2([xs, ys], [x2, y2], -angle);
3492
+ const [x4, y4] = rotate2([xs, ys], [x2, y2], angle);
3493
+ if (head === "diamond" || head === "diamond_outline") {
3494
+ let o;
3495
+ if (position === "start") {
3496
+ const [qx, qy] = pts.length > 1 ? pts[1] : [0, 0];
3497
+ o = rotate2([x2 + minSize * 2, y2], [x2, y2], Math.atan2(qy - y2, qx - x2));
3498
+ } else {
3499
+ const [qx, qy] = pts.length > 1 ? pts.at(-2) : [0, 0];
3500
+ o = rotate2([x2 - minSize * 2, y2], [x2, y2], Math.atan2(y2 - qy, x2 - qx));
3501
+ }
3502
+ return [x2, y2, x3, y3, o[0], o[1], x4, y4];
3503
+ }
3504
+ return [x2, y2, x3, y3, x4, y4];
3505
+ }
3506
+ function arrowheadShapes(e, shape2, position, head, options, bg) {
3507
+ const pts = arrowheadPoints(e, shape2, position, head);
3508
+ if (!pts) return [];
3509
+ const o = { ...options };
3510
+ switch (head) {
3511
+ case "dot":
3512
+ case "circle":
3513
+ case "circle_outline": {
3514
+ delete o.strokeLineDash;
3515
+ return [gen.circle(pts[0], pts[1], pts[2], { ...o, fill: head === "circle_outline" ? bg : e.strokeColor, fillStyle: "solid", stroke: e.strokeColor, roughness: Math.min(0.5, o.roughness || 0) })];
3516
+ }
3517
+ case "triangle":
3518
+ case "triangle_outline": {
3519
+ delete o.strokeLineDash;
3520
+ const [x, y, x2, y2, x3, y3] = pts;
3521
+ return [gen.polygon([[x, y], [x2, y2], [x3, y3], [x, y]], { ...o, fill: head === "triangle_outline" ? bg : e.strokeColor, fillStyle: "solid", roughness: Math.min(1, o.roughness || 0) })];
3522
+ }
3523
+ case "diamond":
3524
+ case "diamond_outline": {
3525
+ delete o.strokeLineDash;
3526
+ const [x, y, x2, y2, x3, y3, x4, y4] = pts;
3527
+ return [gen.polygon([[x, y], [x2, y2], [x3, y3], [x4, y4], [x, y]], { ...o, fill: head === "diamond_outline" ? bg : e.strokeColor, fillStyle: "solid", roughness: Math.min(1, o.roughness || 0) })];
3528
+ }
3529
+ case "crowfoot_one": {
3530
+ const [, , x3, y3, x4, y4] = pts;
3531
+ return [gen.line(x3, y3, x4, y4, o)];
3532
+ }
3533
+ default: {
3534
+ const [, , x3, y3, x4, y4] = pts;
3535
+ const [x2, y2] = [pts[0], pts[1]];
3536
+ if (e.strokeStyle === "dotted") o.strokeLineDash = [1.5, 6 + (e.strokeWidth ?? 2) - 1 - 1];
3537
+ else delete o.strokeLineDash;
3538
+ o.roughness = Math.min(1, o.roughness || 0);
3539
+ const out = [gen.line(x3, y3, x2, y2, o), gen.line(x4, y4, x2, y2, o)];
3540
+ if (head === "crowfoot_one_or_many") out.push(...arrowheadShapes(e, shape2, position, "crowfoot_one", options, bg));
3541
+ return out;
3542
+ }
3543
+ }
3544
+ }
3545
+ function shapeOf(e, bg) {
3546
+ switch (e.type) {
3547
+ case "rectangle": {
3548
+ if (e.roundness) {
3549
+ const { width: w, height: h } = e;
3550
+ const r = cornerRadius(Math.min(w, h), e);
3551
+ return [gen.path(`M ${r} 0 L ${w - r} 0 Q ${w} 0, ${w} ${r} L ${w} ${h - r} Q ${w} ${h}, ${w - r} ${h} L ${r} ${h} Q 0 ${h}, 0 ${h - r} L 0 ${r} Q 0 0, ${r} 0`, roughOptions(e, true))];
3552
+ }
3553
+ return [gen.rectangle(0, 0, e.width, e.height, roughOptions(e))];
3554
+ }
3555
+ case "diamond": {
3556
+ const [tX, tY, rX, rY, bX, bY, lX, lY] = diamondPoints(e);
3557
+ if (e.roundness) {
3558
+ const v = cornerRadius(Math.abs(tX - lX), e);
3559
+ const h = cornerRadius(Math.abs(rY - tY), e);
3560
+ return [
3561
+ gen.path(
3562
+ `M ${tX + v} ${tY + h} L ${rX - v} ${rY - h} C ${rX} ${rY}, ${rX} ${rY}, ${rX - v} ${rY + h} L ${bX + v} ${bY - h} C ${bX} ${bY}, ${bX} ${bY}, ${bX - v} ${bY - h} L ${lX + v} ${lY + h} C ${lX} ${lY}, ${lX} ${lY}, ${lX + v} ${lY - h} L ${tX - v} ${tY + h} C ${tX} ${tY}, ${tX} ${tY}, ${tX + v} ${tY + h}`,
3563
+ roughOptions(e, true)
3564
+ )
3565
+ ];
3566
+ }
3567
+ return [gen.polygon([[tX, tY], [rX, rY], [bX, bY], [lX, lY]], roughOptions(e))];
3568
+ }
3569
+ case "ellipse":
3570
+ return [gen.ellipse(e.width / 2, e.height / 2, e.width, e.height, roughOptions(e))];
3571
+ case "line":
3572
+ case "arrow": {
3573
+ const o = roughOptions(e);
3574
+ const points = e.points?.length ? e.points : [[0, 0]];
3575
+ let shapes;
3576
+ if (e.elbowed) shapes = [gen.path(elbowPath(points, 16), roughOptions(e, true))];
3577
+ else if (!e.roundness) shapes = [o.fill ? gen.polygon(points, o) : gen.linearPath(points, o)];
3578
+ else shapes = [gen.curve(points, o)];
3579
+ if (e.type === "arrow" && shapes.length) {
3580
+ const { startArrowhead = null, endArrowhead = "arrow" } = e;
3581
+ if (startArrowhead) shapes.push(...arrowheadShapes(e, shapes[0], "start", startArrowhead, o, bg));
3582
+ if (endArrowhead) shapes.push(...arrowheadShapes(e, shapes[0], "end", endArrowhead, o, bg));
3583
+ }
3584
+ return shapes;
3585
+ }
3586
+ case "freedraw":
3587
+ return isPathALoop(e.points ?? []) ? [gen.curve(e.points, { ...roughOptions(e), stroke: "none" })] : [];
3588
+ default:
3589
+ return [];
3590
+ }
3591
+ }
3592
+ var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3593
+ var attrs = (a2) => Object.entries(a2).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => ` ${k}="${esc(String(v))}"`).join("");
3594
+ function drawableToSvg(d, extra = {}) {
3595
+ const o = d.options;
3596
+ const parts = d.sets.map((set) => {
3597
+ const path = gen.opsToPath(set, DECIMALS);
3598
+ if (set.type === "path")
3599
+ return `<path${attrs({ d: path, stroke: o.stroke, "stroke-width": o.strokeWidth, fill: "none", "stroke-dasharray": o.strokeLineDash?.join(" ") })}/>`;
3600
+ if (set.type === "fillPath")
3601
+ return `<path${attrs({ d: path, stroke: "none", "stroke-width": 0, fill: o.fill, "fill-rule": d.shape === "curve" || d.shape === "polygon" ? "evenodd" : void 0 })}/>`;
3602
+ const fw = o.fillWeight < 0 ? o.strokeWidth / 2 : o.fillWeight;
3603
+ return `<path${attrs({ d: path, stroke: o.fill, "stroke-width": fw, fill: "none", "stroke-dasharray": o.fillLineDash?.join(" ") })}/>`;
3604
+ });
3605
+ return `<g${attrs(extra)}>${parts.join("")}</g>`;
3606
+ }
3607
+ function freedrawPath(e) {
3608
+ const pts = e.simulatePressure ? e.points : e.points?.length ? e.points.map(([x, y], i) => [x, y, e.pressures?.[i] ?? 0.5]) : [[0, 0, 0.5]];
3609
+ const outline = ae(pts, {
3610
+ simulatePressure: e.simulatePressure,
3611
+ size: (e.strokeWidth ?? 2) * 4.25,
3612
+ thinning: 0.6,
3613
+ smoothing: 0.5,
3614
+ streamline: 0.5,
3615
+ easing: (t) => Math.sin(t * Math.PI / 2),
3616
+ last: !!e.lastCommittedPoint
3617
+ });
3618
+ if (!outline.length) return "";
3619
+ const med = (a2, b2) => [(a2[0] + b2[0]) / 2, (a2[1] + b2[1]) / 2];
3620
+ const d = ["M", outline[0], "Q"];
3621
+ outline.forEach((p, i) => {
3622
+ if (i === outline.length - 1) d.push(p, med(p, outline[0]), "L", outline[0], "Z");
3623
+ else d.push(p, med(p, outline[i + 1]));
3624
+ });
3625
+ return d.map((x) => typeof x === "string" ? x : `${r2(x[0])},${r2(x[1])}`).join(" ");
3626
+ }
3627
+ var RTL = /[֐-߿יִ-﷽ﹰ-ﻼ]/;
3628
+ function textSvg(e, transform, opacity) {
3629
+ const lines = String(e.text ?? "").replace(/\r\n?/g, "\n").split("\n");
3630
+ const lineHeightPx = e.fontSize * (e.lineHeight ?? 1.25);
3631
+ const m = METRICS[e.fontFamily] ?? METRICS[1];
3632
+ const em = e.fontSize / m.unitsPerEm;
3633
+ const verticalOffset = em * m.ascender + (lineHeightPx - em * m.ascender + em * m.descender) / 2;
3634
+ const hOffset = e.textAlign === "center" ? e.width / 2 : e.textAlign === "right" ? e.width : 0;
3635
+ const direction = RTL.test(e.text ?? "") ? "rtl" : "ltr";
3636
+ const anchor = e.textAlign === "center" ? "middle" : e.textAlign === "right" || direction === "rtl" ? "end" : "start";
3637
+ const body = lines.map(
3638
+ (l2, i) => `<text${attrs({
3639
+ x: r2(hOffset),
3640
+ y: r2(i * lineHeightPx + verticalOffset),
3641
+ "font-family": fontFamilyCss(e.fontFamily),
3642
+ "font-size": `${e.fontSize}px`,
3643
+ fill: e.strokeColor,
3644
+ "text-anchor": anchor,
3645
+ style: "white-space: pre;",
3646
+ direction,
3647
+ "dominant-baseline": "alphabetic"
3648
+ })}>${esc(l2)}</text>`
3649
+ ).join("");
3650
+ return `<g${attrs({ transform, "stroke-opacity": opacity !== 1 ? opacity : void 0, "fill-opacity": opacity !== 1 ? opacity : void 0 })}>${body}</g>`;
3651
+ }
3652
+ function frameLabel(f) {
3653
+ const fontSize = 14;
3654
+ const height = fontSize * 1.25;
3655
+ const name = f.name ?? (f.type === "magicframe" ? "AI Frame" : "Frame");
3656
+ const maxChars = Math.max(1, Math.floor(f.width / (fontSize * 0.55)));
3657
+ const text2 = name.length > maxChars ? `${name.slice(0, Math.max(1, maxChars - 1))}\u2026` : name;
3658
+ return {
3659
+ id: `${f.id}.label`,
3660
+ type: "text",
3661
+ x: f.x,
3662
+ y: f.y - 3 - height,
3663
+ width: Math.min(f.width, text2.length * fontSize * 0.55),
3664
+ height,
3665
+ angle: 0,
3666
+ text: text2,
3667
+ fontSize,
3668
+ fontFamily: 2,
3669
+ lineHeight: 1.25,
3670
+ textAlign: "left",
3671
+ strokeColor: "#999999",
3672
+ opacity: 100,
3673
+ version: 1,
3674
+ versionNonce: 0,
3675
+ isDeleted: false
3676
+ };
3677
+ }
3678
+ function exportSvg(input, files, opts = {}) {
3679
+ const bgColor = opts.backgroundColor ?? "#ffffff";
3680
+ const padding = opts.padding ?? 10;
3681
+ const live = input.filter((e) => !e.isDeleted).map((e, i) => ({ e, i })).sort((a2, b2) => a2.e.index && b2.e.index ? a2.e.index < b2.e.index ? -1 : a2.e.index > b2.e.index ? 1 : 0 : a2.i - b2.i).map((x) => x.e);
3682
+ const byId = new Map(live.map((e) => [e.id, e]));
3683
+ const frames = live.filter(isFrame);
3684
+ const elements = [];
3685
+ for (const e of live) {
3686
+ if (isFrame(e)) elements.push(frameLabel(e));
3687
+ elements.push(e);
3688
+ }
3689
+ const roots = elements.filter((e) => isFrame(e) || !e.frameId || !byId.has(e.frameId));
3690
+ let [minX, minY, maxX, maxY] = [Infinity, Infinity, -Infinity, -Infinity];
3691
+ for (const e of roots) {
3692
+ const [a2, b2, c, d] = elementBounds2(e);
3693
+ minX = Math.min(minX, a2);
3694
+ minY = Math.min(minY, b2);
3695
+ maxX = Math.max(maxX, c);
3696
+ maxY = Math.max(maxY, d);
3697
+ }
3698
+ if (!Number.isFinite(minX)) [minX, minY, maxX, maxY] = [0, 0, 0, 0];
3699
+ const width = maxX - minX + padding * 2;
3700
+ const height = maxY - minY + padding * 2;
3701
+ const ox = -minX + padding;
3702
+ const oy = -minY + padding;
3703
+ const scale = opts.scale ?? 1;
3704
+ const defs = [];
3705
+ const body = [];
3706
+ const clipIds = /* @__PURE__ */ new Map();
3707
+ frames.forEach((f, i) => {
3708
+ const id2 = `frame-clip-${i}`;
3709
+ clipIds.set(f.id, id2);
3710
+ const [x1, , x2] = localBounds(f);
3711
+ const cx = (x2 - x1) / 2;
3712
+ const cy = f.height / 2;
3713
+ defs.push(`<clipPath id="${id2}"><rect${attrs({ transform: `translate(${r2(f.x + ox)} ${r2(f.y + oy)}) rotate(${f.angle ?? 0} ${cx} ${cy})`, width: f.width, height: f.height, rx: 8, ry: 8 })}/></clipPath>`);
3714
+ });
3715
+ const clip = (e, svg2) => {
3716
+ const id2 = e.frameId && clipIds.get(e.frameId);
3717
+ return id2 ? `<g clip-path="url(#${id2})">${svg2}</g>` : svg2;
3718
+ };
3719
+ const imageSymbols = /* @__PURE__ */ new Set();
3720
+ const render = (e) => {
3721
+ const [x1, y1, x2, y2] = localBounds(e);
3722
+ const cx = (x2 - x1) / 2 + x1;
3723
+ const cy = (y2 - y1) / 2 + y1;
3724
+ const deg = 180 * (e.angle ?? 0) / Math.PI;
3725
+ let tx = e.x + ox;
3726
+ let ty = e.y + oy;
3727
+ const frame2 = e.frameId ? byId.get(e.frameId) : void 0;
3728
+ const opacity = (frame2?.opacity ?? 100) * (e.opacity ?? 100) / 1e4;
3729
+ const op = opacity !== 1 ? { "stroke-opacity": opacity, "fill-opacity": opacity } : {};
3730
+ const transform = () => `translate(${r2(tx)} ${r2(ty)}) rotate(${r2(deg)} ${r2(cx)} ${r2(cy)})`;
3731
+ switch (e.type) {
3732
+ case "rectangle":
3733
+ case "diamond":
3734
+ case "ellipse": {
3735
+ const [shape2] = shapeOf(e, bgColor);
3736
+ body.push(clip(e, drawableToSvg(shape2, { ...op, "stroke-linecap": "round", transform: transform() })));
3737
+ return;
3738
+ }
3739
+ case "line":
3740
+ case "arrow": {
3741
+ const label2 = e.boundElements?.find((b2) => b2.type === "text");
3742
+ const t = label2 && byId.get(label2.id);
3743
+ let mask = "";
3744
+ if (t) {
3745
+ mask = `<mask id="mask-${esc(e.id)}"><rect x="0" y="0" fill="#fff" width="${r2(e.width + 100 + tx)}" height="${r2(e.height + 100 + ty)}"/><rect fill="#000" x="${r2(tx + t.x - e.x)}" y="${r2(ty + t.y - e.y)}" width="${r2(t.width)}" height="${r2(t.height)}" opacity="1"/></mask>`;
3746
+ }
3747
+ const loopFill = e.type === "line" && isPathALoop(e.points ?? []) && e.backgroundColor !== "transparent" ? { "fill-rule": "evenodd" } : {};
3748
+ const g = shapeOf(e, bgColor).map((s) => drawableToSvg(s, { ...op, ...loopFill, transform: transform() })).join("");
3749
+ body.push(clip(e, `<g${attrs({ mask: t ? `url(#mask-${e.id})` : void 0, "stroke-linecap": "round" })}>${g}</g>${mask}`));
3750
+ return;
3751
+ }
3752
+ case "freedraw": {
3753
+ const [fill2] = shapeOf(e, bgColor);
3754
+ const path = `<path fill="${esc(e.strokeColor)}" d="${freedrawPath(e)}"/>`;
3755
+ const inner = fill2 ? drawableToSvg(fill2).replace(/<\/g>$/, `${path}</g>`) : `<g>${path}</g>`;
3756
+ body.push(clip(e, inner.replace(/^<g/, `<g${attrs({ ...op, transform: transform(), stroke: "none" })}`)));
3757
+ return;
3758
+ }
3759
+ case "image": {
3760
+ const file = e.fileId && files[e.fileId];
3761
+ if (!file) return;
3762
+ const symbolId = `image-${file.id}`;
3763
+ if (!imageSymbols.has(symbolId)) {
3764
+ imageSymbols.add(symbolId);
3765
+ defs.unshift(`<symbol id="${esc(symbolId)}" viewBox="0 0 1 1" preserveAspectRatio="none"><image href="${esc(file.dataURL)}" preserveAspectRatio="none" width="1" height="1"/></symbol>`);
3766
+ }
3767
+ const flip = e.scale && (e.scale[0] !== 1 || e.scale[1] !== 1) ? `translate(${cx} ${cy}) scale(${e.scale[0]} ${e.scale[1]}) translate(${-cx} ${-cy})` : void 0;
3768
+ let g = `<g${attrs({ transform: transform() })}><use${attrs({ href: `#${symbolId}`, width: Math.round(e.width), height: Math.round(e.height), opacity, transform: flip })}/></g>`;
3769
+ if (e.roundness) {
3770
+ const r = cornerRadius(Math.min(e.width, e.height), e);
3771
+ const id2 = `image-clip-${esc(e.id)}`;
3772
+ defs.push(`<clipPath id="${id2}"><rect width="${e.width}" height="${e.height}" rx="${r}" ry="${r}"/></clipPath>`);
3773
+ g = g.replace(/^<g/, `<g clip-path="url(#${id2})"`);
3774
+ }
3775
+ body.push(clip(e, g));
3776
+ return;
3777
+ }
3778
+ case "frame":
3779
+ case "magicframe":
3780
+ body.push(`<rect${attrs({ transform: transform(), width: `${e.width}px`, height: `${e.height}px`, rx: 8, ry: 8, fill: "none", stroke: "#bbb", "stroke-width": 2 })}/>`);
3781
+ return;
3782
+ case "text": {
3783
+ body.push(clip(e, textSvg(e, transform(), opacity)));
3784
+ return;
3785
+ }
3786
+ default: {
3787
+ if (e.type === "embeddable" || e.type === "iframe") {
3788
+ body.push(`<rect${attrs({ transform: transform(), width: e.width, height: e.height, fill: "#d3d3d3", stroke: "#bbb" })}/>`);
3789
+ }
3790
+ }
3791
+ }
3792
+ };
3793
+ for (const e of elements) {
3794
+ if (e.type === "text" && e.containerId && byId.has(e.containerId)) continue;
3795
+ render(e);
3796
+ const label2 = e.boundElements?.find((b2) => b2.type === "text");
3797
+ const t = label2 && byId.get(label2.id);
3798
+ if (t) render(t);
3799
+ }
3800
+ const style = opts.fontCss ? `<style class="style-fonts">${opts.fontCss}</style>` : "";
3801
+ const bg = opts.background !== false ? `<rect x="0" y="0" width="${r2(width)}" height="${r2(height)}" fill="${esc(bgColor)}"/>` : "";
3802
+ const svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${r2(width)} ${r2(height)}" width="${r2(width * scale)}" height="${r2(height * scale)}"><!-- svg-source:vizo --><defs>${defs.join("")}${style}</defs>${bg}${body.join("")}</svg>`;
3803
+ return { svg, width, height };
3804
+ }
3805
+
3806
+ // src/ttf.ts
3807
+ import { readFileSync } from "node:fs";
3808
+ function loadFace(path) {
3809
+ const b2 = readFileSync(path);
3810
+ const tables = /* @__PURE__ */ new Map();
3811
+ for (let i = 0; i < b2.readUInt16BE(4); i++) tables.set(b2.toString("latin1", 12 + i * 16, 16 + i * 16), b2.readUInt32BE(12 + i * 16 + 8));
3812
+ const head = tables.get("head");
3813
+ const unitsPerEm = b2.readUInt16BE(head + 18);
3814
+ const numberOfHMetrics = b2.readUInt16BE(tables.get("hhea") + 34);
3815
+ const hmtx = tables.get("hmtx");
3816
+ const glyphOf = cmap(b2, tables.get("cmap"));
3817
+ const width = (g) => b2.readUInt16BE(hmtx + Math.min(g, numberOfHMetrics - 1) * 4);
3818
+ return {
3819
+ has: (cp) => glyphOf(cp) > 0,
3820
+ advance: (cp) => width(glyphOf(cp)) / unitsPerEm
3821
+ };
3822
+ }
3823
+ function cmap(b2, at) {
3824
+ let f4 = -1;
3825
+ let f12 = -1;
3826
+ for (let i = 0; i < b2.readUInt16BE(at + 2); i++) {
3827
+ const r = at + 4 + i * 8;
3828
+ const [platform, encoding] = [b2.readUInt16BE(r), b2.readUInt16BE(r + 2)];
3829
+ if (!(platform === 0 || platform === 3 && (encoding === 1 || encoding === 10))) continue;
3830
+ const sub = at + b2.readUInt32BE(r + 4);
3831
+ const format = b2.readUInt16BE(sub);
3832
+ if (format === 12) f12 = sub;
3833
+ else if (format === 4) f4 = sub;
3834
+ }
3835
+ if (f12 >= 0) {
3836
+ const n = b2.readUInt32BE(f12 + 12);
3837
+ return (cp) => {
3838
+ let [lo, hi] = [0, n - 1];
3839
+ while (lo <= hi) {
3840
+ const mid = lo + hi >> 1;
3841
+ const g = f12 + 16 + mid * 12;
3842
+ const [start, end] = [b2.readUInt32BE(g), b2.readUInt32BE(g + 4)];
3843
+ if (cp < start) hi = mid - 1;
3844
+ else if (cp > end) lo = mid + 1;
3845
+ else return b2.readUInt32BE(g + 8) + cp - start;
3846
+ }
3847
+ return 0;
3848
+ };
3849
+ }
3850
+ if (f4 < 0) return () => 0;
3851
+ const segX2 = b2.readUInt16BE(f4 + 6);
3852
+ const ends = f4 + 14;
3853
+ const starts = ends + segX2 + 2;
3854
+ const deltas = starts + segX2;
3855
+ const offsets = deltas + segX2;
3856
+ return (cp) => {
3857
+ if (cp > 65535) return 0;
3858
+ for (let i = 0; i < segX2; i += 2) {
3859
+ if (cp > b2.readUInt16BE(ends + i)) continue;
3860
+ const start = b2.readUInt16BE(starts + i);
3861
+ if (cp < start) return 0;
3862
+ const delta = b2.readInt16BE(deltas + i);
3863
+ const ro = b2.readUInt16BE(offsets + i);
3864
+ if (!ro) return cp + delta & 65535;
3865
+ const g = b2.readUInt16BE(offsets + i + ro + (cp - start) * 2);
3866
+ return g ? g + delta & 65535 : 0;
3867
+ }
3868
+ return 0;
3869
+ };
3870
+ }
3871
+
3872
+ // src/export.ts
3873
+ var EXPORT_TOOL = {
3874
+ name: "export_image",
3875
+ title: "Export image",
3876
+ description: "Save a board, or some of its diagrams/elements, as a PNG or SVG file on this machine, drawn the way the board shows it (hand-drawn strokes, fonts, images). Returns the file path, and by default a small preview image so you can check the result.",
3877
+ inputSchema: {
3878
+ type: "object",
3879
+ properties: {
3880
+ boardId: { type: "string", description: "Board id (e.g. 'b_xxx') or full board URL" },
3881
+ format: { type: "string", enum: ["png", "svg"], description: "Default png" },
3882
+ diagramIds: { type: "array", items: { type: "string" }, description: "Only these diagrams (ids from get_board), with their frame and title" },
3883
+ elementIds: { type: "array", items: { type: "string" }, description: "Only these elements; a frame brings its contents, a shape its label" },
3884
+ scale: { type: "number", minimum: 0.1, maximum: 8, description: "PNG pixels per board unit (default 2 = sharp on high-DPI screens)" },
3885
+ background: { type: "boolean", description: "Fill with the board background (default true); false gives a transparent PNG/SVG" },
3886
+ path: { type: "string", description: "Output file. Default: <board title>.<format> in the current directory (never overwrites; adds -2, -3\u2026)" },
3887
+ preview: { type: "boolean", description: "Also return a small PNG preview to look at (default true)" }
3888
+ },
3889
+ required: ["boardId"],
3890
+ additionalProperties: false
3891
+ },
3892
+ annotations: { readOnlyHint: false, destructiveHint: false }
3893
+ };
3894
+ var MAX_SIDE = 16384;
3895
+ var MAX_AREA = 16384 * 16384;
3896
+ var PREVIEW_MAX_SIDE = 1200;
3897
+ var ExportError = class extends Error {
3898
+ };
3899
+ function pickElements(scene, args) {
3900
+ const all = scene.elements.filter((e) => !e.isDeleted);
3901
+ if (!args.diagramIds?.length && !args.elementIds?.length) return all;
3902
+ const byId = new Map(all.map((e) => [e.id, e]));
3903
+ const want = /* @__PURE__ */ new Set();
3904
+ const unknown = [];
3905
+ for (const d of args.diagramIds ?? []) {
3906
+ const diagram = scene.diagrams.find((x) => x.id === d);
3907
+ if (diagram && byId.has(diagram.frameElementId)) want.add(diagram.frameElementId);
3908
+ else unknown.push(`diagram '${d}'`);
3909
+ }
3910
+ for (const id2 of args.elementIds ?? []) {
3911
+ if (byId.has(id2)) want.add(id2);
3912
+ else unknown.push(`element '${id2}'`);
3913
+ }
3914
+ if (unknown.length) throw new ExportError(`Not on this board: ${unknown.join(", ")}. Use get_board for ids.`);
3915
+ for (const e of all) if (e.frameId && want.has(e.frameId)) want.add(e.id);
3916
+ for (const e of all) if (e.type === "text" && e.containerId && want.has(e.containerId)) want.add(e.id);
3917
+ return all.filter((e) => want.has(e.id));
3918
+ }
3919
+ var FONT_DIR = new URL("./fonts/", import.meta.url);
3920
+ var manifest = null;
3921
+ var fonts = () => manifest ??= JSON.parse(readFileSync2(new URL("fonts.json", FONT_DIR), "utf8"));
3922
+ function inRange(range, cp) {
3923
+ if (!range) return true;
3924
+ return range.split(",").some((r) => {
3925
+ const [a2, b2] = r.trim().replace(/^U\+/i, "").split("-");
3926
+ const lo = parseInt(a2, 16);
3927
+ return cp >= lo && cp <= (b2 ? parseInt(b2, 16) : lo);
3928
+ });
3929
+ }
3930
+ function fontCss(elements) {
3931
+ const used = fontsUsed(elements);
3932
+ const chars = /* @__PURE__ */ new Map();
3933
+ for (const e of elements) {
3934
+ const id2 = e.type === "text" ? e.fontFamily ?? 5 : e.type === "frame" || e.type === "magicframe" ? 2 : null;
3935
+ if (id2 === null) continue;
3936
+ const set = chars.get(id2) ?? /* @__PURE__ */ new Set();
3937
+ for (const ch of String(e.type === "text" ? e.text : e.name ?? "Frame")) set.add(ch.codePointAt(0));
3938
+ chars.set(id2, set);
3939
+ }
3940
+ return fonts().filter((f) => used.has(f.id) && [...chars.get(f.id) ?? []].some((cp) => inRange(f.unicodeRange, cp))).map((f) => {
3941
+ const data = readFileSync2(new URL(f.file, FONT_DIR)).toString("base64");
3942
+ return `@font-face { font-family: "${f.family}"; src: url(data:font/woff2;base64,${data}); ${f.unicodeRange ? `unicode-range: ${f.unicodeRange}; ` : ""}}`;
3943
+ }).join("\n");
3944
+ }
3945
+ var unescapeXml = (s) => s.replace(/&(lt|gt|quot|amp);/g, (_, e) => ({ lt: "<", gt: ">", quot: '"', amp: "&" })[e]);
3946
+ var escapeXml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3947
+ var BORROW_ORDER = ["Virgil", "Excalifont", "Comic Shanns", "Liberation Sans", "Nunito", "Cascadia", "Lilita One"];
3948
+ var ttfPath = (f) => fileURLToPath(new URL(f.file.replace(/\.woff2$/, ".ttf"), FONT_DIR));
3949
+ var faces = /* @__PURE__ */ new Map();
3950
+ var face = (f) => faces.get(f.file) ?? faces.set(f.file, loadFace(ttfPath(f))).get(f.file);
3951
+ function fontFor(family, cp) {
3952
+ const own = fonts().filter((f) => f.family === family);
3953
+ const others = fonts().filter((f) => f.family !== family).sort((a2, b2) => BORROW_ORDER.indexOf(a2.family) - BORROW_ORDER.indexOf(b2.family));
3954
+ return [...own, ...others].find((f) => face(f).has(cp)) ?? null;
3955
+ }
3956
+ function guessAdvance(cp) {
3957
+ if (cp === 8205 || cp >= 65024 && cp <= 65039) return 0;
3958
+ const wide = cp >= 4352 && cp <= 4447 || cp >= 11904 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65280 && cp <= 65376 || cp >= 126976;
3959
+ return wide ? 1 : 0.8;
3960
+ }
3961
+ var setAttr = (attrs2, name, value) => new RegExp(` ${name}="[^"]*"`).test(attrs2) ? attrs2.replace(new RegExp(` ${name}="[^"]*"`), ` ${name}="${value}"`) : `${attrs2} ${name}="${value}"`;
3962
+ var EMOJI = new RegExp("\\p{Extended_Pictographic}", "u");
3963
+ function useTtfFamilies(svg) {
3964
+ return svg.replace(/<text([^>]*?) font-family="([^"]*)"([^>]*)>([^<]*)<\/text>/g, (_, pre, list, post, body) => {
3965
+ let family = list.split(",")[0].trim();
3966
+ if (family === "Helvetica") family = "Liberation Sans";
3967
+ const attrs2 = `${pre}${post} xml:space="preserve"`;
3968
+ const runs = [];
3969
+ for (const ch of unescapeXml(body)) {
3970
+ const cp = ch.codePointAt(0);
3971
+ const last = runs.at(-1);
3972
+ const joiner = cp === 8205 || cp >= 65024 && cp <= 65039 || cp >= 127995 && cp <= 127999;
3973
+ if (last && (ch === " " || joiner)) {
3974
+ last.text += ch;
3975
+ continue;
3976
+ }
3977
+ const emoji = EMOJI.test(ch) && cp > 255;
3978
+ const font = emoji ? null : fontFor(family, cp);
3979
+ if (last && last.font === font && last.emoji === emoji) last.text += ch;
3980
+ else runs.push({ font, emoji, text: ch });
3981
+ }
3982
+ const fontName = (r) => r.font?.ttfFamily ?? family;
3983
+ if (runs.length === 1 && !runs[0].emoji) return `<text${attrs2} font-family="${fontName(runs[0])}">${body}</text>`;
3984
+ if (!runs.length || / direction="rtl"/.test(attrs2)) return `<text${attrs2} font-family="${family}">${body}</text>`;
3985
+ const size = parseFloat(/font-size="([\d.]+)/.exec(attrs2)?.[1] ?? "20");
3986
+ const x = parseFloat(/ x="([-\d.]+)"/.exec(attrs2)?.[1] ?? "0");
3987
+ const anchor = / text-anchor="(\w+)"/.exec(attrs2)?.[1] ?? "start";
3988
+ const widths = runs.map((r) => [...r.text].reduce((w, ch) => w + (r.font ? face(r.font).advance(ch.codePointAt(0)) : guessAdvance(ch.codePointAt(0))), 0) * size);
3989
+ const total = widths.reduce((a2, b2) => a2 + b2, 0);
3990
+ let at = anchor === "middle" ? x - total / 2 : anchor === "end" ? x - total : x;
3991
+ return runs.map((r, i) => {
3992
+ const a2 = setAttr(setAttr(attrs2, "x", Math.round(at * 100) / 100), "text-anchor", "start");
3993
+ at += widths[i];
3994
+ return r.emoji ? "" : `<text${a2} font-family="${fontName(r)}">${escapeXml(r.text)}</text>`;
3995
+ }).join("");
3996
+ });
3997
+ }
3998
+ async function rasterize(svg, width) {
3999
+ const { Resvg } = await import("@resvg/resvg-js");
4000
+ svg = useTtfFamilies(svg);
4001
+ const fontFiles = fonts().map(ttfPath);
4002
+ const png = new Resvg(svg, {
4003
+ fitTo: { mode: "width", value: Math.max(1, Math.round(width)) },
4004
+ // System fonts cover what Excalidraw's own fonts don't (CJK, emoji); Helvetica falls back to Liberation Sans.
4005
+ font: { fontFiles, loadSystemFonts: true, defaultFontFamily: "Liberation Sans", sansSerifFamily: "Liberation Sans" },
4006
+ imageRendering: 0,
4007
+ shapeRendering: 2,
4008
+ textRendering: 1
4009
+ }).render();
4010
+ return { data: png.asPng(), width: png.width, height: png.height };
4011
+ }
4012
+ function outputPath(requested, title, format) {
4013
+ if (requested) {
4014
+ const p2 = resolve(requested);
4015
+ return extname(p2).toLowerCase() === `.${format}` ? p2 : `${p2}.${format}`;
4016
+ }
4017
+ const base2 = (title || "board").replace(/[\\/:*?"<>|\x00-\x1f]+/g, "-").trim() || "board";
4018
+ let p = resolve(`${base2}.${format}`);
4019
+ for (let n = 2; existsSync(p); n++) p = resolve(`${base2}-${n}.${format}`);
4020
+ return p;
4021
+ }
4022
+ async function exportImage(scene, args) {
4023
+ const format = args.format ?? "png";
4024
+ const elements = pickElements(scene, args);
4025
+ if (!elements.length) throw new ExportError("Nothing to export: the board is empty.");
4026
+ const files = Object.fromEntries(scene.files.map((f) => [f.id, f]));
4027
+ const background = args.background !== false;
4028
+ const scale = args.scale ?? 2;
4029
+ const svgOpts = { background, backgroundColor: "#ffffff", fontCss: fontCss(elements) };
4030
+ const { svg, width, height } = exportSvg(elements, files, { ...svgOpts, scale: format === "svg" ? 1 : scale });
4031
+ const path = outputPath(args.path, scene.title ?? "", format);
4032
+ mkdirSync(dirname(path), { recursive: true });
4033
+ let size;
4034
+ let bytes;
4035
+ if (format === "svg") {
4036
+ writeFileSync(path, svg);
4037
+ size = { width: Math.round(width), height: Math.round(height) };
4038
+ bytes = Buffer.byteLength(svg);
4039
+ } else {
4040
+ const w = Math.round(width * scale);
4041
+ const h = Math.round(height * scale);
4042
+ if (w > MAX_SIDE || h > MAX_SIDE || w * h > MAX_AREA)
4043
+ throw new ExportError(`${w}\xD7${h} px is too large (max ${MAX_SIDE} px per side). Use a smaller scale, or export fewer diagrams.`);
4044
+ const png = await rasterize(svg, w);
4045
+ writeFileSync(path, png.data);
4046
+ size = { width: png.width, height: png.height };
4047
+ bytes = png.data.length;
4048
+ }
4049
+ const content = [
4050
+ { type: "text", text: JSON.stringify({ path, format, ...size, bytes, elements: elements.length }, null, 1) }
4051
+ ];
4052
+ if (args.preview !== false) {
4053
+ const fit = Math.min(1, PREVIEW_MAX_SIDE / Math.max(width, height));
4054
+ const preview = await rasterize(svg, width * fit);
4055
+ content.push({ type: "image", data: Buffer.from(preview.data).toString("base64"), mimeType: "image/png" });
4056
+ }
4057
+ return { content };
4058
+ }
4059
+
4060
+ // src/index.ts
4061
+ var PROXY_HEADER = "X-Vizo-Proxy";
4062
+ var PUT_TOOL = "put_diagram_elements";
4063
+ var SCENE_TOOL = "get_board_scene";
4064
+ var SIZES_TOOL = "get_image_sizes";
4065
+ var HIDDEN = /* @__PURE__ */ new Set([PUT_TOOL, SCENE_TOOL, SIZES_TOOL]);
4066
+ var IMAGE_TOOL = "add_image";
4067
+ var MAX_IMAGE_BYTES = 47e5;
4068
+ var LOCAL_TOOLS = /* @__PURE__ */ new Set(["draw_diagram", "update_diagram"]);
4069
+ var toolError = (text2) => ({ content: [{ type: "text", text: text2 }], isError: true });
4070
+ function arg(name) {
4071
+ const i = process.argv.indexOf(`--${name}`);
4072
+ if (i >= 0) return process.argv[i + 1];
4073
+ const eq = process.argv.find((a2) => a2.startsWith(`--${name}=`));
4074
+ return eq?.slice(name.length + 3);
4075
+ }
4076
+ var log = (...a2) => console.error("[vizo-mcp]", ...a2);
4077
+ async function main() {
4078
+ const base2 = (arg("url") ?? process.env.VIZO_URL ?? "").replace(/\/$/, "");
4079
+ const token = arg("token") ?? process.env.VIZO_TOKEN;
4080
+ if (!base2 || !token) {
4081
+ log("usage: vizo-mcp --url <https://your-vizo-host> --token <vz_\u2026> (or VIZO_URL / VIZO_TOKEN)");
4082
+ log("Create a token at <url>/settings.");
4083
+ process.exit(2);
4084
+ }
4085
+ const endpoint = new URL(base2.endsWith("/mcp") ? base2 : `${base2}/mcp`);
4086
+ const upstream = new Client({ name: "vizo-mcp-proxy", version: "0.3.0" });
4087
+ await upstream.connect(
4088
+ new StreamableHTTPClientTransport(endpoint, { requestInit: { headers: { Authorization: `Bearer ${token}`, [PROXY_HEADER]: String(LAYOUT_VERSION) } } })
4089
+ );
4090
+ const caps = upstream.getServerCapabilities() ?? {};
4091
+ log(`connected to ${endpoint.href} (local layout v${LAYOUT_VERSION})`);
4092
+ const server = new Server(
4093
+ { name: "vizo", version: "0.3.0" },
4094
+ {
4095
+ capabilities: {
4096
+ tools: caps.tools ? {} : void 0,
4097
+ resources: caps.resources ? {} : void 0,
4098
+ prompts: caps.prompts ? {} : void 0
4099
+ },
4100
+ instructions: `${upstream.getInstructions() ?? ""}
4101
+
4102
+ This local proxy also has export_image: save a board or some of its diagrams as PNG/SVG on this machine (e.g. for docs or a PR), and look at the returned preview to check your drawing.`.trim()
4103
+ }
4104
+ );
4105
+ if (caps.tools) {
4106
+ let serverSupportsLocal;
4107
+ let serverSupportsExport = false;
4108
+ server.setRequestHandler(ListToolsRequestSchema, async (req) => {
4109
+ const r = await upstream.listTools(req.params);
4110
+ if (r.tools.some((t) => t.name === PUT_TOOL)) serverSupportsLocal = true;
4111
+ serverSupportsExport = r.tools.some((t) => t.name === SCENE_TOOL);
4112
+ const tools = r.tools.filter((t) => !HIDDEN.has(t.name)).map((t) => t.name === IMAGE_TOOL ? withPath(t) : t);
4113
+ return { ...r, tools: serverSupportsExport && !r.nextCursor ? [...tools, EXPORT_TOOL] : tools };
4114
+ });
4115
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
4116
+ const { name, arguments: args = {} } = req.params;
4117
+ if (name === EXPORT_TOOL.name) return exportLocally(args);
4118
+ if (name === IMAGE_TOOL && typeof args.path === "string") {
4119
+ const { path, ...rest } = args;
4120
+ let dataURL;
4121
+ try {
4122
+ dataURL = readImage(path);
4123
+ } catch (e) {
4124
+ return toolError(e instanceof Error ? e.message : String(e));
4125
+ }
4126
+ return upstream.callTool({ ...req.params, arguments: { ...rest, dataURL } });
4127
+ }
4128
+ if (!LOCAL_TOOLS.has(name)) return upstream.callTool(req.params);
4129
+ serverSupportsLocal ??= (await upstream.listTools()).tools.some((t) => t.name === PUT_TOOL);
4130
+ if (!serverSupportsLocal) {
4131
+ return toolError(
4132
+ `This Vizo server (${base2}) does not accept locally rendered diagrams (no ${PUT_TOOL}); it is older than this vizo-mcp (layout v${LAYOUT_VERSION}). Nothing was drawn. Update the server, or use a vizo-mcp version matching it.`
4133
+ );
4134
+ }
4135
+ return renderLocally(name, args);
4136
+ });
4137
+ }
4138
+ if (caps.resources) {
4139
+ server.setRequestHandler(ListResourcesRequestSchema, (req) => upstream.listResources(req.params));
4140
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, (req) => upstream.listResourceTemplates(req.params));
4141
+ server.setRequestHandler(ReadResourceRequestSchema, (req) => upstream.readResource(req.params));
4142
+ }
4143
+ if (caps.prompts) {
4144
+ server.setRequestHandler(ListPromptsRequestSchema, (req) => upstream.listPrompts(req.params));
4145
+ server.setRequestHandler(GetPromptRequestSchema, (req) => upstream.getPrompt(req.params));
4146
+ }
4147
+ async function renderLocally(name, args) {
4148
+ let spec;
4149
+ try {
4150
+ spec = parseSpec(args.spec);
4151
+ } catch (e) {
4152
+ if (e instanceof SpecError) return toolError(e.message);
4153
+ throw e;
4154
+ }
4155
+ const drawing = name === "draw_diagram";
4156
+ const diagramId = drawing ? `d-${randomId(8)}` : String(args.diagramId ?? "");
4157
+ if (!diagramId) return toolError("diagramId is required");
4158
+ const imageIds = specImageIds(spec);
4159
+ let images = {};
4160
+ if (imageIds.length) {
4161
+ const res = await upstream.callTool({ name: SIZES_TOOL, arguments: { boardId: args.boardId, fileIds: imageIds } });
4162
+ const body = res.content?.find((c) => c.type === "text")?.text ?? "";
4163
+ if (res.isError) return toolError(body || "Couldn't read the board's images.");
4164
+ images = Object.fromEntries(JSON.parse(body).map((f) => [f.id, { width: f.width, height: f.height }]));
4165
+ }
4166
+ let r;
4167
+ try {
4168
+ r = await renderDiagram(spec, { diagramId, images });
4169
+ } catch (e) {
4170
+ if (e instanceof SpecError) return toolError(e.message);
4171
+ throw e;
4172
+ }
4173
+ return upstream.callTool({
4174
+ name: PUT_TOOL,
4175
+ arguments: {
4176
+ boardId: args.boardId,
4177
+ mode: drawing ? "draw" : "update",
4178
+ layoutVersion: LAYOUT_VERSION,
4179
+ diagramId,
4180
+ spec,
4181
+ placement: args.placement,
4182
+ elements: r.elements
4183
+ }
4184
+ });
4185
+ }
4186
+ async function exportLocally(args) {
4187
+ if (!args?.boardId) return toolError("boardId is required");
4188
+ const r = await upstream.callTool({ name: SCENE_TOOL, arguments: { boardId: args.boardId } });
4189
+ const body = r.content?.find((c) => c.type === "text")?.text ?? "";
4190
+ if (r.isError) return toolError(body || "Couldn't read the board.");
4191
+ try {
4192
+ return await exportImage(JSON.parse(body), args);
4193
+ } catch (e) {
4194
+ if (e instanceof ExportError) return toolError(e.message);
4195
+ log("export failed:", e);
4196
+ return toolError(`Export failed: ${e instanceof Error ? e.message : String(e)}`);
4197
+ }
4198
+ }
4199
+ await server.connect(new StdioServerTransport());
4200
+ const shutdown = async () => {
4201
+ await Promise.allSettled([server.close(), upstream.close()]);
4202
+ process.exit(0);
4203
+ };
4204
+ process.on("SIGINT", shutdown);
4205
+ process.on("SIGTERM", shutdown);
4206
+ process.stdin.on("close", shutdown);
4207
+ }
4208
+ function withPath(t) {
4209
+ const schema = t.inputSchema ?? { type: "object", properties: {} };
4210
+ return {
4211
+ ...t,
4212
+ description: `${t.description ?? ""} Here you can also pass a local file \`path\` instead of url/dataURL.`,
4213
+ inputSchema: {
4214
+ ...schema,
4215
+ properties: { path: { type: "string", description: "Image file on this machine (absolute, or relative to the working directory); use instead of url/dataURL" }, ...schema.properties }
4216
+ }
4217
+ };
4218
+ }
4219
+ var MIME = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", bmp: "image/bmp", svg: "image/svg+xml" };
4220
+ function readImage(path) {
4221
+ const full = resolve2(path);
4222
+ let size;
4223
+ try {
4224
+ size = statSync(full).size;
4225
+ } catch {
4226
+ throw new Error(`No such file: ${full}`);
4227
+ }
4228
+ if (size > MAX_IMAGE_BYTES) throw new Error(`Image too large (${(size / 1e6).toFixed(1)} MB; max ${MAX_IMAGE_BYTES / 1e6} MB). Use a smaller or more compressed image.`);
4229
+ const mime = MIME[full.split(".").pop()?.toLowerCase() ?? ""] ?? "application/octet-stream";
4230
+ return `data:${mime};base64,${readFileSync3(full).toString("base64")}`;
1409
4231
  }
1410
4232
  main().catch((e) => {
1411
4233
  log(e instanceof Error ? e.message : e);