schematex 0.8.0 → 0.8.1

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 (81) hide show
  1. package/dist/ai/ai-sdk.cjs +8 -8
  2. package/dist/ai/ai-sdk.d.cts +4 -4
  3. package/dist/ai/ai-sdk.d.ts +4 -4
  4. package/dist/ai/ai-sdk.js +3 -3
  5. package/dist/ai/index.cjs +14 -14
  6. package/dist/ai/index.d.cts +3 -3
  7. package/dist/ai/index.d.ts +3 -3
  8. package/dist/ai/index.js +3 -3
  9. package/dist/{api-BnJAPCwC.d.cts → api-BDMaX1cT.d.cts} +1 -1
  10. package/dist/{api-DAZJGq9r.d.ts → api-OED2jUVj.d.ts} +1 -1
  11. package/dist/browser.cjs +9 -9
  12. package/dist/browser.d.cts +3 -3
  13. package/dist/browser.d.ts +3 -3
  14. package/dist/browser.js +3 -3
  15. package/dist/{chunk-2YBBDPOW.cjs → chunk-AJJYWXZB.cjs} +2 -2
  16. package/dist/chunk-AJJYWXZB.cjs.map +1 -0
  17. package/dist/{chunk-3CSST5GZ.cjs → chunk-HBQFUZBD.cjs} +973 -94
  18. package/dist/chunk-HBQFUZBD.cjs.map +1 -0
  19. package/dist/{chunk-XI5QP7LM.js → chunk-J6AIKEVV.js} +972 -94
  20. package/dist/chunk-J6AIKEVV.js.map +1 -0
  21. package/dist/{chunk-6DVK5M2J.js → chunk-KF3DFASA.js} +2 -2
  22. package/dist/chunk-KF3DFASA.js.map +1 -0
  23. package/dist/{chunk-RKN6QJ7K.cjs → chunk-Q3SJB7H3.cjs} +132 -7
  24. package/dist/chunk-Q3SJB7H3.cjs.map +1 -0
  25. package/dist/{chunk-TXMT4XLE.js → chunk-YWYWVKRB.js} +130 -5
  26. package/dist/chunk-YWYWVKRB.js.map +1 -0
  27. package/dist/{diagnostics-B-jOt6Ua.d.cts → diagnostics-hObcaaFC.d.cts} +1 -1
  28. package/dist/{diagnostics-B-jOt6Ua.d.ts → diagnostics-hObcaaFC.d.ts} +1 -1
  29. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  30. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  31. package/dist/diagrams/circuit/index.d.cts +1 -1
  32. package/dist/diagrams/circuit/index.d.ts +1 -1
  33. package/dist/diagrams/ecomap/index.d.cts +1 -1
  34. package/dist/diagrams/ecomap/index.d.ts +1 -1
  35. package/dist/diagrams/entity/index.d.cts +1 -1
  36. package/dist/diagrams/entity/index.d.ts +1 -1
  37. package/dist/diagrams/fishbone/index.d.cts +1 -1
  38. package/dist/diagrams/fishbone/index.d.ts +1 -1
  39. package/dist/diagrams/flowchart/index.d.cts +2 -2
  40. package/dist/diagrams/flowchart/index.d.ts +2 -2
  41. package/dist/diagrams/genogram/index.d.cts +1 -1
  42. package/dist/diagrams/genogram/index.d.ts +1 -1
  43. package/dist/diagrams/ladder/index.d.cts +1 -1
  44. package/dist/diagrams/ladder/index.d.ts +1 -1
  45. package/dist/diagrams/logic/index.d.cts +1 -1
  46. package/dist/diagrams/logic/index.d.ts +1 -1
  47. package/dist/diagrams/orgchart/index.d.cts +1 -1
  48. package/dist/diagrams/orgchart/index.d.ts +1 -1
  49. package/dist/diagrams/pedigree/index.d.cts +1 -1
  50. package/dist/diagrams/pedigree/index.d.ts +1 -1
  51. package/dist/diagrams/phylo/index.d.cts +1 -1
  52. package/dist/diagrams/phylo/index.d.ts +1 -1
  53. package/dist/diagrams/sld/index.cjs +7 -7
  54. package/dist/diagrams/sld/index.d.cts +1 -1
  55. package/dist/diagrams/sld/index.d.ts +1 -1
  56. package/dist/diagrams/sld/index.js +1 -1
  57. package/dist/diagrams/sociogram/index.d.cts +1 -1
  58. package/dist/diagrams/sociogram/index.d.ts +1 -1
  59. package/dist/diagrams/timing/index.d.cts +1 -1
  60. package/dist/diagrams/timing/index.d.ts +1 -1
  61. package/dist/diagrams/venn/index.d.cts +1 -1
  62. package/dist/diagrams/venn/index.d.ts +1 -1
  63. package/dist/{index-D-MAB6CH.d.cts → index-B0YO7rx8.d.cts} +1 -1
  64. package/dist/{index-8MaUFTMN.d.ts → index-u3KZBdas.d.ts} +1 -1
  65. package/dist/index.cjs +39 -35
  66. package/dist/index.d.cts +7 -5
  67. package/dist/index.d.ts +7 -5
  68. package/dist/index.js +4 -4
  69. package/dist/react.cjs +3 -3
  70. package/dist/react.d.cts +2 -2
  71. package/dist/react.d.ts +2 -2
  72. package/dist/react.js +2 -2
  73. package/dist/{tools-CV1JZ75-.d.cts → tools-CVgUmiGP.d.cts} +2 -2
  74. package/dist/{tools-CcPysgAD.d.ts → tools-CbBjmZVr.d.ts} +2 -2
  75. package/package.json +1 -1
  76. package/dist/chunk-2YBBDPOW.cjs.map +0 -1
  77. package/dist/chunk-3CSST5GZ.cjs.map +0 -1
  78. package/dist/chunk-6DVK5M2J.js.map +0 -1
  79. package/dist/chunk-RKN6QJ7K.cjs.map +0 -1
  80. package/dist/chunk-TXMT4XLE.js.map +0 -1
  81. package/dist/chunk-XI5QP7LM.js.map +0 -1
@@ -2,7 +2,7 @@ import { orgchart } from './chunk-E3CAJGJM.js';
2
2
  import { circuit } from './chunk-T3GV7OVF.js';
3
3
  import { blockdiagram } from './chunk-DX44TBFZ.js';
4
4
  import { ladder } from './chunk-JGCKW5RS.js';
5
- import { sld } from './chunk-6DVK5M2J.js';
5
+ import { sld } from './chunk-KF3DFASA.js';
6
6
  import { entity } from './chunk-WNGOG4CI.js';
7
7
  import { fishbone } from './chunk-YTGOLTLJ.js';
8
8
  import { venn } from './chunk-UOM3EDE6.js';
@@ -142,8 +142,8 @@ function parseInfluence(src, title2) {
142
142
  const key = tok.slice(0, eq).toLowerCase();
143
143
  const value = tok.slice(eq + 1);
144
144
  if (key === "utility" || key === "payoff" || key === "value") {
145
- const num3 = Number(value);
146
- if (!Number.isNaN(num3)) utility = num3;
145
+ const num4 = Number(value);
146
+ if (!Number.isNaN(num4)) utility = num4;
147
147
  } else if (key === "kind") {
148
148
  const resolved = toKind(value.toLowerCase());
149
149
  if (!resolved) throw new DTreeParseError(`Unknown node kind "${value}"`, line2);
@@ -11642,8 +11642,8 @@ var SequenceLayout = class {
11642
11642
  }
11643
11643
  }
11644
11644
  const row = { message: m, y, x1, x2, self };
11645
- const num3 = this.nextNumber();
11646
- if (num3 !== void 0) row.number = num3;
11645
+ const num4 = this.nextNumber();
11646
+ if (num4 !== void 0) row.number = num4;
11647
11647
  if (self) {
11648
11648
  row.selfBottomY = y + SEQ_CONST.SELF_GAP;
11649
11649
  this.y = row.selfBottomY;
@@ -12411,7 +12411,7 @@ function parseMarking(body, lineNo, ast, placeIds) {
12411
12411
  if (!placeIds.has(id)) {
12412
12412
  throw new PetriParseError(`marking references unknown place "${id}"`, lineNo);
12413
12413
  }
12414
- const place = ast.places.find((pl) => pl.id === id);
12414
+ const place = ast.places.find((pl2) => pl2.id === id);
12415
12415
  if (place) place.tokens = n;
12416
12416
  }
12417
12417
  }
@@ -21561,7 +21561,7 @@ function classPeriod(P, states) {
21561
21561
  level.set(start, 0);
21562
21562
  const queue = [start];
21563
21563
  let g = 0;
21564
- const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
21564
+ const gcd2 = (a, b) => b === 0 ? a : gcd2(b, a % b);
21565
21565
  while (queue.length > 0) {
21566
21566
  const u = queue.shift();
21567
21567
  const lu = level.get(u);
@@ -21572,7 +21572,7 @@ function classPeriod(P, states) {
21572
21572
  queue.push(j);
21573
21573
  } else {
21574
21574
  const diff = lu + 1 - level.get(j);
21575
- g = gcd(g, Math.abs(diff));
21575
+ g = gcd2(g, Math.abs(diff));
21576
21576
  }
21577
21577
  }
21578
21578
  }
@@ -24359,9 +24359,9 @@ function nodePrefix(node) {
24359
24359
  const m = /^([A-Za-z]+)(-?\d+)?$/.exec(node.trim());
24360
24360
  if (!m) return node;
24361
24361
  const letters = m[1];
24362
- const num3 = m[2];
24363
- if (num3 === void 0 || num3 === "0" || num3 === "-0") return letters;
24364
- return `${letters}${num3}`;
24362
+ const num4 = m[2];
24363
+ if (num4 === void 0 || num4 === "0" || num4 === "-0") return letters;
24364
+ return `${letters}${num4}`;
24365
24365
  }
24366
24366
  function assignIcomCodes(ast) {
24367
24367
  const counters = { I: 0, C: 0, O: 0, M: 0 };
@@ -25646,6 +25646,560 @@ var threatmodel = {
25646
25646
  }
25647
25647
  };
25648
25648
 
25649
+ // src/diagrams/welding/types.ts
25650
+ var GROOVE_TYPES = /* @__PURE__ */ new Set([
25651
+ "square",
25652
+ "vgroove",
25653
+ "bevel",
25654
+ "ugroove",
25655
+ "jgroove",
25656
+ "flarev",
25657
+ "flarebevel"
25658
+ ]);
25659
+ var WELD_TYPE_NAMES = {
25660
+ fillet: "fillet weld",
25661
+ square: "square-groove weld",
25662
+ vgroove: "V-groove weld",
25663
+ bevel: "bevel-groove weld",
25664
+ ugroove: "U-groove weld",
25665
+ jgroove: "J-groove weld",
25666
+ flarev: "flare-V-groove weld",
25667
+ flarebevel: "flare-bevel-groove weld",
25668
+ plug: "plug weld",
25669
+ slot: "slot weld",
25670
+ spot: "spot weld",
25671
+ seam: "seam weld",
25672
+ back: "back weld",
25673
+ backing: "backing weld",
25674
+ surfacing: "surfacing weld",
25675
+ edge: "edge weld"
25676
+ };
25677
+ function validateWelding(ast) {
25678
+ const warnings = [];
25679
+ ast.joints.forEach((j, i) => {
25680
+ const id = j.label ? `joint "${j.label}"` : `joint ${i + 1}`;
25681
+ if (!j.arrow && !j.other) {
25682
+ warnings.push(`${id}: no weld declared (add arrow:, other:, or both:).`);
25683
+ }
25684
+ for (const [side, spec] of [
25685
+ ["arrow", j.arrow],
25686
+ ["other", j.other]
25687
+ ]) {
25688
+ if (!spec) continue;
25689
+ if (spec.type === "fillet" && spec.size === void 0) {
25690
+ warnings.push(`${id} ${side}: a fillet weld needs a leg size (size=\u2026).`);
25691
+ }
25692
+ if ((spec.type === "plug" || spec.type === "slot") && spec.size === void 0) {
25693
+ warnings.push(`${id} ${side}: a ${spec.type} weld needs a diameter (size=\u2026).`);
25694
+ }
25695
+ if (spec.type === "surfacing" && spec.throat === void 0) {
25696
+ warnings.push(`${id} ${side}: a surfacing weld needs a build-up height (throat=\u2026).`);
25697
+ }
25698
+ if (spec.angle !== void 0 && !GROOVE_TYPES.has(spec.type)) {
25699
+ warnings.push(`${id} ${side}: angle= only applies to groove welds, not a ${spec.type}.`);
25700
+ }
25701
+ if (spec.pitch !== void 0 && spec.length === void 0) {
25702
+ warnings.push(`${id} ${side}: pitch= needs a length= (an intermittent weld is length-pitch).`);
25703
+ }
25704
+ if (spec.angle !== void 0 && (spec.angle <= 0 || spec.angle >= 180)) {
25705
+ warnings.push(`${id} ${side}: groove angle ${spec.angle}\xB0 is out of range (0\u2013180).`);
25706
+ }
25707
+ }
25708
+ if (j.other?.type === "surfacing") {
25709
+ warnings.push(`${id}: a surfacing weld is arrow-side only.`);
25710
+ }
25711
+ });
25712
+ return warnings;
25713
+ }
25714
+
25715
+ // src/diagrams/welding/parser.ts
25716
+ var WELD_TYPES = /* @__PURE__ */ new Set([
25717
+ "fillet",
25718
+ "square",
25719
+ "vgroove",
25720
+ "bevel",
25721
+ "ugroove",
25722
+ "jgroove",
25723
+ "flarev",
25724
+ "flarebevel",
25725
+ "plug",
25726
+ "slot",
25727
+ "spot",
25728
+ "seam",
25729
+ "back",
25730
+ "backing",
25731
+ "surfacing",
25732
+ "edge"
25733
+ ]);
25734
+ var TYPE_ALIAS = {
25735
+ v: "vgroove",
25736
+ "v-groove": "vgroove",
25737
+ u: "ugroove",
25738
+ "u-groove": "ugroove",
25739
+ j: "jgroove",
25740
+ "j-groove": "jgroove",
25741
+ "flare-v": "flarev",
25742
+ "flare-bevel": "flarebevel",
25743
+ groove: "vgroove"
25744
+ };
25745
+ var CONTOURS = /* @__PURE__ */ new Set(["flush", "convex", "concave"]);
25746
+ var FINISHES = /* @__PURE__ */ new Set(["G", "M", "C", "R", "H", "U"]);
25747
+ function stripQuotes5(s) {
25748
+ const t = s.trim();
25749
+ const pairs = [['"', '"'], ["'", "'"], ["\u201C", "\u201D"], ["\u2018", "\u2019"]];
25750
+ for (const [a, b] of pairs) {
25751
+ if (t.startsWith(a) && t.endsWith(b) && t.length >= 2) return t.slice(1, -1);
25752
+ }
25753
+ return t;
25754
+ }
25755
+ function num3(v) {
25756
+ const n = Number(v);
25757
+ return Number.isFinite(n) ? n : void 0;
25758
+ }
25759
+ function parseWeldSpec(raw) {
25760
+ const tokens = raw.trim().split(/\s+/);
25761
+ if (tokens.length === 0) return void 0;
25762
+ const head = tokens[0].toLowerCase();
25763
+ const type = WELD_TYPES.has(head) ? head : TYPE_ALIAS[head];
25764
+ if (!type) return void 0;
25765
+ const spec = { type };
25766
+ for (let i = 1; i < tokens.length; i++) {
25767
+ const m = tokens[i].match(/^([a-zA-Z]+)\s*[=:]\s*(.+)$/);
25768
+ if (!m) continue;
25769
+ const key = m[1].toLowerCase();
25770
+ const val = m[2];
25771
+ switch (key) {
25772
+ case "size":
25773
+ case "leg":
25774
+ case "s":
25775
+ spec.size = num3(val);
25776
+ break;
25777
+ case "len":
25778
+ case "length":
25779
+ case "l":
25780
+ spec.length = num3(val);
25781
+ break;
25782
+ case "pitch":
25783
+ case "p":
25784
+ spec.pitch = num3(val);
25785
+ break;
25786
+ case "count":
25787
+ case "n":
25788
+ spec.count = num3(val);
25789
+ break;
25790
+ case "angle":
25791
+ case "a":
25792
+ case "deg":
25793
+ spec.angle = num3(val);
25794
+ break;
25795
+ case "root":
25796
+ case "gap":
25797
+ case "r":
25798
+ spec.root = num3(val);
25799
+ break;
25800
+ case "throat":
25801
+ case "e":
25802
+ case "t":
25803
+ spec.throat = num3(val);
25804
+ break;
25805
+ case "contour":
25806
+ case "c":
25807
+ if (CONTOURS.has(val.toLowerCase())) spec.contour = val.toLowerCase();
25808
+ break;
25809
+ case "finish":
25810
+ case "f": {
25811
+ const u = val.toUpperCase();
25812
+ if (FINISHES.has(u)) spec.finish = u;
25813
+ break;
25814
+ }
25815
+ }
25816
+ }
25817
+ return spec;
25818
+ }
25819
+ function emptyJoint() {
25820
+ return { around: false, field: false };
25821
+ }
25822
+ function stripComment13(line2) {
25823
+ const hash = line2.indexOf("#");
25824
+ if (hash >= 0 && (line2.slice(0, hash).match(/["'“‘]/g)?.length ?? 0) % 2 === 0) {
25825
+ return line2.slice(0, hash);
25826
+ }
25827
+ return line2;
25828
+ }
25829
+ var DIR_BOUNDARY = "(?:arrow|other|both|tail|label)\\s*[:=]|(?:around|all-?around|field|site)\\b";
25830
+ function parseJointBody(body, joint) {
25831
+ const dirRe = new RegExp(`\\b(arrow|other|both|tail|label)\\s*[:=]\\s*([\\s\\S]*?)(?=\\b(?:${DIR_BOUNDARY})|$)`, "gi");
25832
+ for (const m of body.matchAll(dirRe)) {
25833
+ const key = m[1].toLowerCase();
25834
+ const val = m[2].trim();
25835
+ if (key === "arrow") joint.arrow = parseWeldSpec(val);
25836
+ else if (key === "other") joint.other = parseWeldSpec(val);
25837
+ else if (key === "both") {
25838
+ const spec = parseWeldSpec(val);
25839
+ if (spec) {
25840
+ joint.arrow = spec;
25841
+ joint.other = { ...spec };
25842
+ }
25843
+ } else if (key === "tail") joint.tail = stripQuotes5(val);
25844
+ else if (key === "label") joint.label = stripQuotes5(val);
25845
+ }
25846
+ if (/\b(around|all-?around)\b/i.test(body)) joint.around = true;
25847
+ if (/\b(field|site)\b/i.test(body)) joint.field = true;
25848
+ }
25849
+ function parseWelding(text2) {
25850
+ const ast = { type: "welding", standard: "aws", joints: [], warnings: [] };
25851
+ const src = text2.split(/\r?\n/).map(stripComment13).join("\n");
25852
+ const headEnd = src.search(/\bjoint\b/i);
25853
+ const headerScope = headEnd >= 0 ? src.slice(0, headEnd) : src;
25854
+ const header = headerScope.match(/welding\b([^\n]*)/i);
25855
+ if (header) {
25856
+ const rest = header[1].trim();
25857
+ const std = rest.match(/standard\s*[:=]\s*([a-zA-Z-]+)/i);
25858
+ if (std) {
25859
+ const s = std[1].toLowerCase();
25860
+ if (s === "aws" || s === "iso-a" || s === "iso-b") ast.standard = s;
25861
+ else if (s === "iso") ast.standard = "iso-a";
25862
+ }
25863
+ const titleM = rest.replace(/standard\s*[:=]\s*[a-zA-Z-]+/i, "").trim();
25864
+ if (titleM) ast.title = stripQuotes5(titleM);
25865
+ }
25866
+ const jointRe = /\bjoint\b([^{]*)\{([\s\S]*?)\}/gi;
25867
+ for (const m of src.matchAll(jointRe)) {
25868
+ const joint = emptyJoint();
25869
+ const labelRaw = m[1].trim();
25870
+ if (labelRaw) joint.label = stripQuotes5(labelRaw);
25871
+ parseJointBody(m[2], joint);
25872
+ ast.joints.push(joint);
25873
+ }
25874
+ const opens = (src.match(/\{/g) ?? []).length;
25875
+ const closes = (src.match(/\}/g) ?? []).length;
25876
+ const tail = opens > closes ? src.match(/\bjoint\b([^{]*)\{([^}]*)$/i) : null;
25877
+ if (tail) {
25878
+ const joint = emptyJoint();
25879
+ const labelRaw = tail[1].trim();
25880
+ if (labelRaw) joint.label = stripQuotes5(labelRaw);
25881
+ parseJointBody(tail[2], joint);
25882
+ ast.joints.push(joint);
25883
+ }
25884
+ ast.warnings = validateWelding(ast);
25885
+ return ast;
25886
+ }
25887
+
25888
+ // src/diagrams/welding/layout.ts
25889
+ var PAD = 22;
25890
+ var BAND_H = 132;
25891
+ var REF_X0 = 150;
25892
+ var REF_LEN = 168;
25893
+ var SYMBOL_DX = 96;
25894
+ var REF_Y_OFFSET = 60;
25895
+ var CANVAS_W = 470;
25896
+ function layoutWelding(ast) {
25897
+ const titleH = ast.title ? 38 : 0;
25898
+ const nJoints = Math.max(1, ast.joints.length);
25899
+ const bandsBottom = titleH + nJoints * BAND_H;
25900
+ const warningsY = ast.warnings.length > 0 ? bandsBottom + 6 : 0;
25901
+ const warningsH = ast.warnings.length > 0 ? 20 + ast.warnings.length * 16 : 0;
25902
+ return {
25903
+ canvasWidth: CANVAS_W,
25904
+ canvasHeight: bandsBottom + warningsH + PAD,
25905
+ titleH,
25906
+ bandH: BAND_H,
25907
+ refX0: REF_X0,
25908
+ refX1: REF_X0 + REF_LEN,
25909
+ symbolX: REF_X0 + SYMBOL_DX,
25910
+ refYOffset: REF_Y_OFFSET,
25911
+ warningsY
25912
+ };
25913
+ }
25914
+
25915
+ // src/diagrams/welding/symbols.ts
25916
+ var W = 18;
25917
+ var H = 15;
25918
+ function pl(points, cls) {
25919
+ return polygon({ points: points.map((p) => `${round9(p[0])},${round9(p[1])}`).join(" "), class: cls, fill: "none" });
25920
+ }
25921
+ function round9(n) {
25922
+ return Math.round(n * 100) / 100;
25923
+ }
25924
+ function weldGlyph(type, cx, y, dir, cls) {
25925
+ const x0 = cx - W / 2;
25926
+ const x1 = cx + W / 2;
25927
+ const yb = y + dir * H;
25928
+ switch (type) {
25929
+ case "fillet":
25930
+ return [pl([[x0, y], [x0, yb], [x1, y]], cls)];
25931
+ case "square":
25932
+ return [
25933
+ line({ x1: cx - 3, y1: y, x2: cx - 3, y2: yb, class: cls }),
25934
+ line({ x1: cx + 3, y1: y, x2: cx + 3, y2: yb, class: cls })
25935
+ ];
25936
+ case "vgroove":
25937
+ return [path({ d: `M ${round9(x0)} ${round9(yb)} L ${round9(cx)} ${round9(y)} L ${round9(x1)} ${round9(yb)}`, class: cls, fill: "none" })];
25938
+ case "bevel":
25939
+ return [
25940
+ line({ x1: cx - 4, y1: y, x2: cx - 4, y2: yb, class: cls }),
25941
+ line({ x1: cx - 4, y1: y, x2: x1, y2: yb, class: cls })
25942
+ ];
25943
+ case "ugroove":
25944
+ return [
25945
+ path({
25946
+ d: `M ${round9(x0)} ${round9(y)} L ${round9(x0)} ${round9(y + dir * H * 0.45)} Q ${round9(x0)} ${round9(yb)} ${round9(cx)} ${round9(yb)} Q ${round9(x1)} ${round9(yb)} ${round9(x1)} ${round9(y + dir * H * 0.45)} L ${round9(x1)} ${round9(y)}`,
25947
+ class: cls,
25948
+ fill: "none"
25949
+ })
25950
+ ];
25951
+ case "jgroove":
25952
+ return [
25953
+ path({
25954
+ d: `M ${round9(cx - 4)} ${round9(y)} L ${round9(cx - 4)} ${round9(y + dir * H * 0.45)} Q ${round9(cx - 4)} ${round9(yb)} ${round9(x1)} ${round9(yb)}`,
25955
+ class: cls,
25956
+ fill: "none"
25957
+ })
25958
+ ];
25959
+ case "flarev":
25960
+ return [
25961
+ path({ d: `M ${round9(x0)} ${round9(yb)} Q ${round9(cx - 2)} ${round9(yb)} ${round9(cx)} ${round9(y)}`, class: cls, fill: "none" }),
25962
+ path({ d: `M ${round9(x1)} ${round9(yb)} Q ${round9(cx + 2)} ${round9(yb)} ${round9(cx)} ${round9(y)}`, class: cls, fill: "none" })
25963
+ ];
25964
+ case "flarebevel":
25965
+ return [
25966
+ line({ x1: cx - 4, y1: y, x2: cx - 4, y2: yb, class: cls }),
25967
+ path({ d: `M ${round9(cx - 4)} ${round9(y)} Q ${round9(x1)} ${round9(y)} ${round9(x1)} ${round9(yb)}`, class: cls, fill: "none" })
25968
+ ];
25969
+ case "plug":
25970
+ case "slot": {
25971
+ const ry = dir > 0 ? y : y - H * 0.7;
25972
+ return [rect({ x: x0, y: ry, width: W, height: H * 0.7, class: cls, fill: "none" })];
25973
+ }
25974
+ case "spot":
25975
+ return [circle({ cx, cy: y, r: W * 0.42, class: cls, fill: "none" })];
25976
+ case "seam":
25977
+ return [
25978
+ circle({ cx, cy: y, r: W * 0.42, class: cls, fill: "none" }),
25979
+ line({ x1: cx - W * 0.42, y1: y, x2: cx + W * 0.42, y2: y, class: cls })
25980
+ ];
25981
+ case "back":
25982
+ case "backing": {
25983
+ const rr = W * 0.45;
25984
+ const sweep = dir > 0 ? 1 : 0;
25985
+ return [path({ d: `M ${round9(cx - rr)} ${round9(y)} A ${round9(rr)} ${round9(rr)} 0 0 ${sweep} ${round9(cx + rr)} ${round9(y)}`, class: cls, fill: "none" })];
25986
+ }
25987
+ case "surfacing": {
25988
+ const r6 = W / 4;
25989
+ const yy = y + dir * 3;
25990
+ const bump = (off) => path({ d: `M ${round9(cx - W / 2)} ${round9(off)} a ${round9(r6)} ${round9(r6)} 0 0 ${dir > 0 ? 1 : 0} ${round9(W / 2)} 0 a ${round9(r6)} ${round9(r6)} 0 0 ${dir > 0 ? 1 : 0} ${round9(W / 2)} 0`, class: cls, fill: "none" });
25991
+ return [bump(y), bump(yy)];
25992
+ }
25993
+ case "edge":
25994
+ return [
25995
+ line({ x1: cx - 3, y1: y, x2: cx - 3, y2: yb, class: cls }),
25996
+ line({ x1: cx + 3, y1: y, x2: cx + 3, y2: yb, class: cls })
25997
+ ];
25998
+ }
25999
+ }
26000
+ function contourGlyph(contour, cx, y, dir, cls) {
26001
+ const yy = y + dir * (H + 5);
26002
+ const half = W / 2;
26003
+ if (contour === "flush") {
26004
+ return line({ x1: cx - half, y1: yy, x2: cx + half, y2: yy, class: cls });
26005
+ }
26006
+ const bulge = contour === "convex" ? dir : -dir;
26007
+ return path({
26008
+ d: `M ${round9(cx - half)} ${round9(yy)} Q ${round9(cx)} ${round9(yy + bulge * 5)} ${round9(cx + half)} ${round9(yy)}`,
26009
+ class: cls,
26010
+ fill: "none"
26011
+ });
26012
+ }
26013
+ var WELD_GLYPH_W = W;
26014
+ var WELD_GLYPH_H = H;
26015
+
26016
+ // src/diagrams/welding/renderer.ts
26017
+ var CSS = `
26018
+ .sx-welding { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
26019
+ .sx-welding-title { font: 600 16px sans-serif; fill: #111; text-anchor: middle; }
26020
+ .sx-weld-ref { stroke: #1e3a5f; stroke-width: 1.6; fill: none; }
26021
+ .sx-weld-ref-dashed { stroke: #1e3a5f; stroke-width: 1.4; fill: none; stroke-dasharray: 5 3; }
26022
+ .sx-weld-leader { stroke: #1e3a5f; stroke-width: 1.6; fill: none; }
26023
+ .sx-weld-arrowhead { fill: #1e3a5f; }
26024
+ .sx-weld-glyph { stroke: #1d4ed8; stroke-width: 1.8; fill: none; stroke-linejoin: round; stroke-linecap: round; }
26025
+ .sx-weld-supp { stroke: #1d4ed8; stroke-width: 1.5; fill: none; }
26026
+ .sx-weld-allaround { stroke: #1e3a5f; stroke-width: 1.6; fill: none; }
26027
+ .sx-weld-flag { fill: #1e3a5f; stroke: #1e3a5f; stroke-width: 1; }
26028
+ .sx-weld-dim { font: 600 12px ui-monospace, Menlo, monospace; fill: #0f172a; }
26029
+ .sx-weld-angle { font: 600 11px ui-monospace, Menlo, monospace; fill: #475569; }
26030
+ .sx-weld-finish { font: 700 11px sans-serif; fill: #1d4ed8; text-anchor: middle; }
26031
+ .sx-weld-tailtext { font: 500 11px sans-serif; fill: #334155; text-anchor: end; dominant-baseline: central; }
26032
+ .sx-weld-label { font: 600 12.5px sans-serif; fill: #1f2937; text-anchor: middle; }
26033
+ .sx-weld-warn { font: 500 11.5px sans-serif; fill: #b45309; }
26034
+ .sx-weld-warn-head { font: 700 11.5px sans-serif; fill: #b45309; }
26035
+ `;
26036
+ function r23(n) {
26037
+ return Math.round(n * 100) / 100;
26038
+ }
26039
+ function sizeText(spec) {
26040
+ if (spec.size === void 0 && spec.throat === void 0) return "";
26041
+ const parts = [];
26042
+ if (spec.size !== void 0) parts.push(String(spec.size));
26043
+ if (spec.throat !== void 0) parts.push(`(${spec.throat})`);
26044
+ return parts.join(" ");
26045
+ }
26046
+ function lengthText(spec) {
26047
+ if (spec.length === void 0) return "";
26048
+ if (spec.pitch !== void 0) {
26049
+ return spec.count !== void 0 ? `${spec.count}\xD7${spec.length} (${spec.pitch})` : `${spec.length}-${spec.pitch}`;
26050
+ }
26051
+ return String(spec.length);
26052
+ }
26053
+ function renderSide(spec, symbolX, refY, dir) {
26054
+ const nodes = [];
26055
+ nodes.push(...weldGlyph(spec.type, symbolX, refY, dir, "sx-weld-glyph"));
26056
+ const midY = refY + dir * (WELD_GLYPH_H / 2);
26057
+ const leftX = symbolX - WELD_GLYPH_W / 2;
26058
+ const rightX = symbolX + WELD_GLYPH_W / 2;
26059
+ const sz = sizeText(spec);
26060
+ if (sz) {
26061
+ nodes.push(
26062
+ text({ x: r23(leftX - 5), y: r23(midY), class: "sx-weld-dim", "text-anchor": "end", "dominant-baseline": "central" }, sz)
26063
+ );
26064
+ }
26065
+ const len = lengthText(spec);
26066
+ if (len) {
26067
+ nodes.push(
26068
+ text({ x: r23(rightX + 5), y: r23(midY), class: "sx-weld-dim", "text-anchor": "start", "dominant-baseline": "central" }, len)
26069
+ );
26070
+ }
26071
+ if (spec.angle !== void 0) {
26072
+ nodes.push(
26073
+ text({ x: r23(symbolX), y: r23(refY + dir * (WELD_GLYPH_H + 11)), class: "sx-weld-angle", "text-anchor": "middle" }, `${spec.angle}\xB0`)
26074
+ );
26075
+ }
26076
+ if (spec.root !== void 0) {
26077
+ nodes.push(
26078
+ text({ x: r23(rightX + 5), y: r23(refY + dir * 5), class: "sx-weld-angle", "text-anchor": "start", "dominant-baseline": "central" }, `root ${spec.root}`)
26079
+ );
26080
+ }
26081
+ if (spec.contour) {
26082
+ nodes.push(contourGlyph(spec.contour, symbolX, refY, dir, "sx-weld-supp"));
26083
+ }
26084
+ if (spec.finish) {
26085
+ nodes.push(
26086
+ text({ x: r23(symbolX), y: r23(refY + dir * (WELD_GLYPH_H + 13)), class: "sx-weld-finish" }, spec.finish)
26087
+ );
26088
+ }
26089
+ return nodes;
26090
+ }
26091
+ function renderJoint(joint, ast, bandTop, lay) {
26092
+ const refY = bandTop + lay.refYOffset;
26093
+ const { refX0, refX1, symbolX } = lay;
26094
+ const iso = ast.standard === "iso-a";
26095
+ const nodes = [];
26096
+ nodes.push(line({ x1: refX0, y1: refY, x2: refX1, y2: refY, class: "sx-weld-ref" }));
26097
+ const symmetric = iso && joint.arrow && joint.other && joint.arrow.type === joint.other.type;
26098
+ if (iso && !symmetric) {
26099
+ nodes.push(line({ x1: refX0, y1: refY - 6, x2: refX1, y2: refY - 6, class: "sx-weld-ref-dashed" }));
26100
+ }
26101
+ const lx = refX1 + 34;
26102
+ const ly = refY + 44;
26103
+ nodes.push(line({ x1: refX1, y1: refY, x2: lx, y2: ly, class: "sx-weld-leader" }));
26104
+ const ah = 8;
26105
+ const angle = Math.atan2(ly - refY, lx - refX1);
26106
+ const a1 = angle + 2.7;
26107
+ const a2 = angle - 2.7;
26108
+ nodes.push(
26109
+ polygon({
26110
+ points: `${r23(lx)},${r23(ly)} ${r23(lx + ah * Math.cos(a1))},${r23(ly + ah * Math.sin(a1))} ${r23(lx + ah * Math.cos(a2))},${r23(ly + ah * Math.sin(a2))}`,
26111
+ class: "sx-weld-arrowhead"
26112
+ })
26113
+ );
26114
+ if (joint.arrow) {
26115
+ nodes.push(...renderSide(joint.arrow, symbolX, refY, 1));
26116
+ }
26117
+ if (joint.other && !symmetric) {
26118
+ const oy = iso ? refY - 6 : refY;
26119
+ nodes.push(...renderSide(joint.other, symbolX, oy, -1));
26120
+ }
26121
+ if (joint.around) {
26122
+ nodes.push(circle({ cx: refX1, cy: refY, r: 5, class: "sx-weld-allaround" }));
26123
+ }
26124
+ if (joint.field) {
26125
+ const fx = refX1;
26126
+ const fy = refY;
26127
+ nodes.push(line({ x1: fx, y1: fy, x2: fx, y2: fy - 14, class: "sx-weld-flag" }));
26128
+ nodes.push(polygon({ points: `${fx},${fy - 14} ${fx},${fy - 6} ${fx - 11},${fy - 10}`, class: "sx-weld-flag" }));
26129
+ }
26130
+ if (joint.tail) {
26131
+ nodes.push(line({ x1: refX0, y1: refY, x2: refX0 - 12, y2: refY - 7, class: "sx-weld-ref" }));
26132
+ nodes.push(line({ x1: refX0, y1: refY, x2: refX0 - 12, y2: refY + 7, class: "sx-weld-ref" }));
26133
+ nodes.push(text({ x: refX0 - 16, y: refY, class: "sx-weld-tailtext" }, joint.tail));
26134
+ }
26135
+ if (joint.label) {
26136
+ nodes.push(text({ x: symbolX, y: r23(bandTop + lay.bandH - 16), class: "sx-weld-label" }, joint.label));
26137
+ }
26138
+ const desc2 = describeJoint(joint);
26139
+ return group({ class: "sx-weld-joint", "data-joint": joint.label ?? "" }, [title(desc2), ...nodes]);
26140
+ }
26141
+ function describeJoint(j) {
26142
+ const sides = [];
26143
+ if (j.arrow) sides.push(`arrow side: ${WELD_TYPE_NAMES[j.arrow.type]}${j.arrow.size !== void 0 ? ` size ${j.arrow.size}` : ""}`);
26144
+ if (j.other) sides.push(`other side: ${WELD_TYPE_NAMES[j.other.type]}`);
26145
+ const extra = [j.around ? "all-around" : "", j.field ? "field weld" : "", j.tail ? `tail ${j.tail}` : ""].filter(Boolean);
26146
+ return `${j.label ? `${j.label} \u2014 ` : ""}${sides.join("; ")}${extra.length ? `; ${extra.join("; ")}` : ""}`;
26147
+ }
26148
+ function renderWeldingAST(ast) {
26149
+ const lay = layoutWelding(ast);
26150
+ const body = [];
26151
+ if (ast.title) {
26152
+ body.push(text({ x: lay.canvasWidth / 2, y: 26, class: "sx-welding-title" }, ast.title));
26153
+ }
26154
+ ast.joints.forEach((joint, i) => {
26155
+ const bandTop = lay.titleH + i * lay.bandH;
26156
+ body.push(renderJoint(joint, ast, bandTop, lay));
26157
+ });
26158
+ if (ast.joints.length === 0) {
26159
+ body.push(
26160
+ text({ x: lay.canvasWidth / 2, y: 70, class: "sx-weld-label" }, 'Add a joint, e.g. joint "plate" { arrow: fillet size=8 }')
26161
+ );
26162
+ }
26163
+ if (ast.warnings.length > 0) {
26164
+ body.push(text({ x: 22, y: lay.warningsY + 14, class: "sx-weld-warn-head" }, "\u26A0 Validation:"));
26165
+ ast.warnings.forEach((w, i) => {
26166
+ body.push(text({ x: 22, y: lay.warningsY + 32 + i * 16, class: "sx-weld-warn" }, w));
26167
+ });
26168
+ }
26169
+ return svgRoot(
26170
+ {
26171
+ class: "sx-welding",
26172
+ "data-diagram-type": "welding",
26173
+ "data-standard": ast.standard,
26174
+ width: lay.canvasWidth,
26175
+ height: lay.canvasHeight,
26176
+ viewBox: `0 0 ${lay.canvasWidth} ${lay.canvasHeight}`,
26177
+ role: "graphics-document"
26178
+ },
26179
+ [
26180
+ title(ast.title ? `Welding symbols \u2014 ${escapeXml(ast.title)}` : "Welding symbols"),
26181
+ desc(
26182
+ `Welding-symbol diagram (${ast.standard.toUpperCase()}) \u2014 ${ast.joints.length} joint(s): ${ast.joints.map(describeJoint).join(" \xB7 ")}`
26183
+ ),
26184
+ defs([el("style", {}, CSS)]),
26185
+ ...body
26186
+ ]
26187
+ );
26188
+ }
26189
+ function renderWelding(text2, _config) {
26190
+ return renderWeldingAST(parseWelding(text2));
26191
+ }
26192
+
26193
+ // src/diagrams/welding/index.ts
26194
+ var welding = {
26195
+ type: "welding",
26196
+ detect: (t) => /^\s*welding\b/i.test(t),
26197
+ parse: parseWelding,
26198
+ render(text2, config) {
26199
+ return renderWelding(text2);
26200
+ }
26201
+ };
26202
+
25649
26203
  // src/core/diagnostics.ts
25650
26204
  var ENGINE_BUG_NAMES = /* @__PURE__ */ new Set([
25651
26205
  "ReferenceError",
@@ -27006,7 +27560,7 @@ function newAST() {
27006
27560
  config: { ...DEFAULT_CONFIG }
27007
27561
  };
27008
27562
  }
27009
- function stripQuotes5(s) {
27563
+ function stripQuotes6(s) {
27010
27564
  const t = s.trim();
27011
27565
  if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
27012
27566
  return t.slice(1, -1);
@@ -27042,7 +27596,7 @@ function parseAxis(raw) {
27042
27596
  function parseNumberList2(raw) {
27043
27597
  const t = raw.trim();
27044
27598
  const inner = t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1) : t;
27045
- return inner.split(",").map((s) => stripQuotes5(s.trim())).filter((s) => s.length > 0);
27599
+ return inner.split(",").map((s) => stripQuotes6(s.trim())).filter((s) => s.length > 0);
27046
27600
  }
27047
27601
  function parseProperties2(raw, point) {
27048
27602
  let i = 0;
@@ -27215,6 +27769,41 @@ function parseQfdLine(line2, ast) {
27215
27769
  }
27216
27770
  return false;
27217
27771
  }
27772
+ function parseGenotype(token) {
27773
+ const t = token.trim();
27774
+ if (t.length === 0 || t.length % 2 !== 0) return null;
27775
+ const loci = [];
27776
+ for (let i = 0; i < t.length; i += 2) {
27777
+ const a = t[i];
27778
+ const b = t[i + 1];
27779
+ if (!/[A-Za-z]/.test(a) || !/[A-Za-z]/.test(b)) return null;
27780
+ if (a.toUpperCase() !== b.toUpperCase()) return null;
27781
+ loci.push({ letter: a.toUpperCase(), alleles: [a, b] });
27782
+ }
27783
+ return loci;
27784
+ }
27785
+ function parsePunnettLine(line2, st) {
27786
+ if (!st.punnett) return false;
27787
+ const crossMatch = line2.match(/^(?:cross|parents?)\s*:\s*(.+)$/i);
27788
+ if (crossMatch) {
27789
+ const parts = crossMatch[1].split(/\s*[x×*]\s*/i);
27790
+ if (parts.length === 2) {
27791
+ st.punnett.p1raw = parts[0].trim();
27792
+ st.punnett.p2raw = parts[1].trim();
27793
+ }
27794
+ return true;
27795
+ }
27796
+ const traitMatch = line2.match(/^trait\s+([A-Za-z])\s*:\s*(.+)$/i);
27797
+ if (traitMatch) {
27798
+ const letter = traitMatch[1].toUpperCase();
27799
+ const slash = traitMatch[2].split("/");
27800
+ const dominant = stripQuotes6(slash[0] ?? "");
27801
+ const recessive = slash[1] !== void 0 ? stripQuotes6(slash[1]) : void 0;
27802
+ st.punnett.traits[letter] = { dominant, recessive };
27803
+ return true;
27804
+ }
27805
+ return false;
27806
+ }
27218
27807
  function parseConfigLine(key, value, ast) {
27219
27808
  const k = key.trim().toLowerCase();
27220
27809
  const v = value.trim();
@@ -27242,7 +27831,7 @@ function parseHeader2(line2, ast) {
27242
27831
  ast.cols = Number(heatMatch[1]);
27243
27832
  ast.rows = Number(heatMatch[2]);
27244
27833
  const title2 = heatMatch[3].trim();
27245
- if (title2) ast.title = stripQuotes5(title2);
27834
+ if (title2) ast.title = stripQuotes6(title2);
27246
27835
  return void 0;
27247
27836
  }
27248
27837
  const corrMatch = rest.match(/^correlation\s*(?:(\d+)\s*x\s*(\d+))?\s*(.*)$/i);
@@ -27254,7 +27843,7 @@ function parseHeader2(line2, ast) {
27254
27843
  ast.rows = Number(corrMatch[2]);
27255
27844
  }
27256
27845
  const title2 = corrMatch[3].trim();
27257
- if (title2) ast.title = stripQuotes5(title2);
27846
+ if (title2) ast.title = stripQuotes6(title2);
27258
27847
  return void 0;
27259
27848
  }
27260
27849
  const sipocMatch = rest.match(/^sipoc\b\s*(.*)$/i);
@@ -27265,7 +27854,7 @@ function parseHeader2(line2, ast) {
27265
27854
  ast.rows = 0;
27266
27855
  ast.sipoc = emptySipoc();
27267
27856
  const title2 = sipocMatch[1].trim();
27268
- if (title2) ast.title = stripQuotes5(title2);
27857
+ if (title2) ast.title = stripQuotes6(title2);
27269
27858
  return void 0;
27270
27859
  }
27271
27860
  const qfdMatch = rest.match(/^qfd\b\s*(.*)$/i);
@@ -27274,7 +27863,16 @@ function parseHeader2(line2, ast) {
27274
27863
  ast.grid = "NxM";
27275
27864
  ast.qfd = emptyQfd();
27276
27865
  const title2 = qfdMatch[1].trim();
27277
- if (title2) ast.title = stripQuotes5(title2);
27866
+ if (title2) ast.title = stripQuotes6(title2);
27867
+ return void 0;
27868
+ }
27869
+ const punnettMatch = rest.match(/^punnett\b\s*(.*)$/i);
27870
+ if (punnettMatch) {
27871
+ ast.mode = "punnett";
27872
+ ast.grid = "NxM";
27873
+ ast.punnett = { genes: [], parent1: [], parent2: [] };
27874
+ const title2 = punnettMatch[1].trim();
27875
+ if (title2) ast.title = stripQuotes6(title2);
27278
27876
  return void 0;
27279
27877
  }
27280
27878
  const tokenMatch = rest.match(/^([a-zA-Z0-9_-]+)\s*(.*)$/);
@@ -27282,13 +27880,13 @@ function parseHeader2(line2, ast) {
27282
27880
  const tok = tokenMatch[1].toLowerCase();
27283
27881
  const remainder = tokenMatch[2].trim();
27284
27882
  if (TEMPLATE_NAMES.has(tok)) {
27285
- if (remainder) ast.title = stripQuotes5(remainder);
27883
+ if (remainder) ast.title = stripQuotes6(remainder);
27286
27884
  return tok;
27287
27885
  }
27288
27886
  if (rest.startsWith('"') || rest.startsWith("'")) {
27289
- ast.title = stripQuotes5(rest);
27887
+ ast.title = stripQuotes6(rest);
27290
27888
  } else if (rest.length > 0) {
27291
- ast.title = stripQuotes5(rest);
27889
+ ast.title = stripQuotes6(rest);
27292
27890
  }
27293
27891
  }
27294
27892
  return void 0;
@@ -27309,6 +27907,7 @@ function parseMatrix(text2) {
27309
27907
  }
27310
27908
  if (/^matrix\b/i.test(line2)) {
27311
27909
  templateName = parseHeader2(line2, st.ast);
27910
+ if (st.ast.mode === "punnett" && !st.punnett) st.punnett = { traits: {} };
27312
27911
  continue;
27313
27912
  }
27314
27913
  if (/^config\s*:/i.test(line2)) {
@@ -27324,11 +27923,12 @@ function parseMatrix(text2) {
27324
27923
  inConfig = false;
27325
27924
  }
27326
27925
  if (/^title\s*:/i.test(line2)) {
27327
- st.ast.title = stripQuotes5(line2.replace(/^title\s*:\s*/i, ""));
27926
+ st.ast.title = stripQuotes6(line2.replace(/^title\s*:\s*/i, ""));
27328
27927
  continue;
27329
27928
  }
27330
27929
  if (st.ast.mode === "sipoc" && parseSipocLine(line2, st.ast)) continue;
27331
27930
  if (st.ast.mode === "qfd" && parseQfdLine(line2, st.ast)) continue;
27931
+ if (st.ast.mode === "punnett" && parsePunnettLine(line2, st)) continue;
27332
27932
  if (/^x-axis\s*:/i.test(line2)) {
27333
27933
  st.ast.xAxis = parseAxis(line2.replace(/^x-axis\s*:\s*/i, ""));
27334
27934
  continue;
@@ -27411,7 +28011,7 @@ function parseMatrix(text2) {
27411
28011
  if (qShort) {
27412
28012
  const q = Number(qShort[1]);
27413
28013
  const labelRaw = qShort[2].trim();
27414
- const label = stripQuotes5(labelRaw);
28014
+ const label = stripQuotes6(labelRaw);
27415
28015
  const cell = quadrantToCell(q);
27416
28016
  st.ast.cellLabels.push({ col: cell.col, row: cell.row, label });
27417
28017
  continue;
@@ -27430,7 +28030,7 @@ function parseMatrix(text2) {
27430
28030
  if (st.ast.cols === 3 && st.ast.rows === 3 && st.ast.grid !== "NxM" && st.ast.mode !== "sipoc" && st.ast.mode !== "qfd") {
27431
28031
  st.ast.grid = "3x3";
27432
28032
  }
27433
- if (st.ast.mode === "heatmap" || st.ast.mode === "correlation" || st.ast.mode === "sipoc" || st.ast.mode === "qfd") {
28033
+ if (st.ast.mode === "heatmap" || st.ast.mode === "correlation" || st.ast.mode === "sipoc" || st.ast.mode === "qfd" || st.ast.mode === "punnett") {
27434
28034
  st.ast.grid = "NxM";
27435
28035
  }
27436
28036
  if (st.ast.mode === "sipoc" && st.ast.sipoc) {
@@ -27448,6 +28048,31 @@ function parseMatrix(text2) {
27448
28048
  st.ast.cols = st.ast.qfd.hows.length;
27449
28049
  st.ast.rows = st.ast.qfd.whats.length;
27450
28050
  }
28051
+ if (st.ast.mode === "punnett" && st.punnett && st.ast.punnett) {
28052
+ const sc = st.punnett;
28053
+ const loci1 = sc.p1raw ? parseGenotype(sc.p1raw) : null;
28054
+ const loci2 = sc.p2raw ? parseGenotype(sc.p2raw) : null;
28055
+ if (loci1 && loci2 && loci1.length > 0) {
28056
+ const genes = loci1.map((l) => {
28057
+ const t = sc.traits[l.letter];
28058
+ return {
28059
+ dominant: l.letter,
28060
+ recessive: l.letter.toLowerCase(),
28061
+ ...t?.dominant ? { dominantTrait: t.dominant } : {},
28062
+ ...t?.recessive ? { recessiveTrait: t.recessive } : {}
28063
+ };
28064
+ });
28065
+ const parent1 = loci1.map((l) => [l.alleles[0], l.alleles[1]]);
28066
+ const parent2 = genes.map((g) => {
28067
+ const found = loci2.find((l) => l.letter === g.dominant);
28068
+ return found ? [found.alleles[0], found.alleles[1]] : [g.dominant, g.recessive];
28069
+ });
28070
+ st.ast.punnett = { genes, parent1, parent2 };
28071
+ const dim = Math.pow(2, genes.length);
28072
+ st.ast.cols = dim;
28073
+ st.ast.rows = dim;
28074
+ }
28075
+ }
27451
28076
  return st.ast;
27452
28077
  }
27453
28078
  function findCommentStart(line2) {
@@ -27466,6 +28091,82 @@ function findCommentStart(line2) {
27466
28091
  }
27467
28092
 
27468
28093
  // src/diagrams/matrix/types.ts
28094
+ function gametesOf(parent) {
28095
+ let acc = [""];
28096
+ for (const pair of parent) {
28097
+ const a0 = pair[0] ?? "";
28098
+ const a1 = pair[1] ?? a0;
28099
+ const next = [];
28100
+ for (const g of acc) {
28101
+ next.push(g + a0);
28102
+ next.push(g + a1);
28103
+ }
28104
+ acc = next;
28105
+ }
28106
+ return acc;
28107
+ }
28108
+ function combineGametes(g1, g2, genes) {
28109
+ let genotype = "";
28110
+ let phenotypeKey = "";
28111
+ for (let i = 0; i < genes.length; i++) {
28112
+ const dom = genes[i].dominant;
28113
+ const rec = genes[i].recessive;
28114
+ const a = g1[i] ?? rec;
28115
+ const b = g2[i] ?? rec;
28116
+ const hasDom = a === dom || b === dom;
28117
+ const pair = a === dom ? [a, b] : b === dom ? [b, a] : [a, b];
28118
+ genotype += pair.join("");
28119
+ phenotypeKey += hasDom ? dom : rec;
28120
+ }
28121
+ return { genotype, phenotypeKey };
28122
+ }
28123
+ function phenotypeLabel(key, genes) {
28124
+ const parts = [];
28125
+ for (let i = 0; i < genes.length; i++) {
28126
+ const ch = key[i];
28127
+ const g = genes[i];
28128
+ if (ch === g.dominant) parts.push(g.dominantTrait ?? `${g.dominant}_`);
28129
+ else parts.push(g.recessiveTrait ?? `${g.recessive}${g.recessive}`);
28130
+ }
28131
+ return parts.join(", ");
28132
+ }
28133
+ function computePunnett(data) {
28134
+ const genes = data.genes;
28135
+ const gametes1 = gametesOf(data.parent1);
28136
+ const gametes2 = gametesOf(data.parent2);
28137
+ const grid = [];
28138
+ const genoCount = /* @__PURE__ */ new Map();
28139
+ const phenoCount = /* @__PURE__ */ new Map();
28140
+ for (const g2 of gametes2) {
28141
+ const row = [];
28142
+ for (const g1 of gametes1) {
28143
+ const cell = combineGametes(g1, g2, genes);
28144
+ row.push(cell);
28145
+ genoCount.set(cell.genotype, (genoCount.get(cell.genotype) ?? 0) + 1);
28146
+ phenoCount.set(cell.phenotypeKey, (phenoCount.get(cell.phenotypeKey) ?? 0) + 1);
28147
+ }
28148
+ grid.push(row);
28149
+ }
28150
+ const genotypeRatio = [...genoCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([genotype, count]) => ({ key: genotype, label: genotype, count }));
28151
+ const phenotypeRatio = [...phenoCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([key, count]) => ({ key, label: phenotypeLabel(key, genes), count }));
28152
+ return { gametes1, gametes2, grid, genotypeRatio, phenotypeRatio };
28153
+ }
28154
+ function gcd(a, b) {
28155
+ return b === 0 ? a : gcd(b, a % b);
28156
+ }
28157
+ function reduceRatio(counts) {
28158
+ const divisor = counts.reduce((acc, c) => gcd(acc, c), 0) || 1;
28159
+ return counts.map((c) => c / divisor).join(":");
28160
+ }
28161
+ function punnettFooter(result) {
28162
+ const enumerate = result.genotypeRatio.length <= 4;
28163
+ return {
28164
+ phenotypeRatio: reduceRatio(result.phenotypeRatio.map((p) => p.count)),
28165
+ legend: result.phenotypeRatio,
28166
+ genotypeRatio: reduceRatio(result.genotypeRatio.map((e) => e.count)),
28167
+ genotypeDetail: enumerate ? result.genotypeRatio.map((e) => `${e.count} ${e.label}`).join(", ") : `${result.genotypeRatio.length} distinct genotypes`
28168
+ };
28169
+ }
27469
28170
  function computeQfdImportance(qfd) {
27470
28171
  const raw = qfd.hows.map(() => 0);
27471
28172
  for (const r6 of qfd.relationships) {
@@ -27483,7 +28184,7 @@ function computeQfdImportance(qfd) {
27483
28184
  }
27484
28185
 
27485
28186
  // src/diagrams/matrix/layout.ts
27486
- var CANVAS_W = 720;
28187
+ var CANVAS_W2 = 720;
27487
28188
  var CANVAS_H = 560;
27488
28189
  var PADDING_X = 110;
27489
28190
  var PADDING_Y = 90;
@@ -27542,21 +28243,21 @@ function resolveLabelCollisions(points, plot, mode) {
27542
28243
  }
27543
28244
  return;
27544
28245
  }
27545
- const PAD = 3;
28246
+ const PAD2 = 3;
27546
28247
  for (let iter = 0; iter < 30; iter++) {
27547
28248
  let moved = false;
27548
28249
  for (let i = 0; i < points.length; i++) {
27549
28250
  for (let j = i + 1; j < points.length; j++) {
27550
28251
  const a = points[i].label;
27551
28252
  const b = points[j].label;
27552
- const ax0 = a.lx - a.width / 2 - PAD;
27553
- const ax1 = a.lx + a.width / 2 + PAD;
27554
- const ay0 = a.ly - a.height / 2 - PAD;
27555
- const ay1 = a.ly + a.height / 2 + PAD;
27556
- const bx0 = b.lx - b.width / 2 - PAD;
27557
- const bx1 = b.lx + b.width / 2 + PAD;
27558
- const by0 = b.ly - b.height / 2 - PAD;
27559
- const by1 = b.ly + b.height / 2 + PAD;
28253
+ const ax0 = a.lx - a.width / 2 - PAD2;
28254
+ const ax1 = a.lx + a.width / 2 + PAD2;
28255
+ const ay0 = a.ly - a.height / 2 - PAD2;
28256
+ const ay1 = a.ly + a.height / 2 + PAD2;
28257
+ const bx0 = b.lx - b.width / 2 - PAD2;
28258
+ const bx1 = b.lx + b.width / 2 + PAD2;
28259
+ const by0 = b.ly - b.height / 2 - PAD2;
28260
+ const by1 = b.ly + b.height / 2 + PAD2;
27560
28261
  const overlapX = Math.min(ax1, bx1) - Math.max(ax0, bx0);
27561
28262
  const overlapY = Math.min(ay1, by1) - Math.max(ay0, by0);
27562
28263
  if (overlapX > 0 && overlapY > 0) {
@@ -27650,8 +28351,51 @@ function layoutQfd(ast) {
27650
28351
  footerH
27651
28352
  };
27652
28353
  }
28354
+ var PUNNETT_PAD = 24;
28355
+ function layoutPunnett(ast) {
28356
+ const pd = ast.punnett;
28357
+ const genes = pd?.genes ?? [];
28358
+ const g = Math.max(1, genes.length);
28359
+ const dim = Math.pow(2, g);
28360
+ const genoChars = g * 2;
28361
+ const cell = Math.max(52, genoChars * 11 + 18);
28362
+ const titleH = ast.title ? 40 : 0;
28363
+ const x0 = PUNNETT_PAD;
28364
+ const y0 = PUNNETT_PAD + titleH;
28365
+ const gridRight = x0 + cell + dim * cell;
28366
+ const gridBottom = y0 + cell + dim * cell;
28367
+ const result = pd && genes.length > 0 ? computePunnett(pd) : null;
28368
+ const phenoLines = result ? Math.max(1, result.phenotypeRatio.length) : 1;
28369
+ const footerY = gridBottom + 18;
28370
+ const footerH = 30 + phenoLines * 22 + 26;
28371
+ let footerW = 0;
28372
+ if (result) {
28373
+ const f = punnettFooter(result);
28374
+ const longest = Math.max(
28375
+ `Phenotype ratio ${f.phenotypeRatio}`.length,
28376
+ `Genotype ratio ${f.genotypeRatio} \u2014 ${f.genotypeDetail}`.length,
28377
+ ...f.legend.map((p) => `${p.count} \xD7 ${p.label}`.length + 4)
28378
+ );
28379
+ footerW = x0 + longest * 6.7 + PUNNETT_PAD;
28380
+ }
28381
+ const canvasWidth = Math.max(gridRight + PUNNETT_PAD, footerW, 360);
28382
+ const canvasHeight = footerY + footerH + PUNNETT_PAD;
28383
+ return {
28384
+ canvasWidth,
28385
+ canvasHeight,
28386
+ x0,
28387
+ y0,
28388
+ headerW: cell,
28389
+ headerH: cell,
28390
+ cellW: cell,
28391
+ cellH: cell,
28392
+ dim,
28393
+ footerY,
28394
+ footerH
28395
+ };
28396
+ }
27653
28397
  function layoutMatrix(ast) {
27654
- const canvasWidth = CANVAS_W;
28398
+ const canvasWidth = CANVAS_W2;
27655
28399
  const canvasHeight = CANVAS_H;
27656
28400
  const plot = {
27657
28401
  x0: PADDING_X,
@@ -27722,7 +28466,7 @@ var HEAT_RAMP = [
27722
28466
  "#ef4444",
27723
28467
  "#b91c1c"
27724
28468
  ];
27725
- var CSS = `
28469
+ var CSS2 = `
27726
28470
  .sx-matrix { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
27727
28471
  .sx-matrix-title { font: 600 16px sans-serif; fill: #111; }
27728
28472
  .sx-matrix-grid { stroke: #e5e7eb; stroke-width: 1; fill: none; }
@@ -27778,6 +28522,18 @@ var CSS = `
27778
28522
  .sx-qfd-imp-value { font: 700 13px sans-serif; fill: #1e3a8a; text-anchor: middle; dominant-baseline: central; }
27779
28523
  .sx-qfd-imp-value-top { font: 800 14px sans-serif; fill: #dc2626; text-anchor: middle; dominant-baseline: central; }
27780
28524
  .sx-qfd-dir { font: 700 13px sans-serif; fill: #475569; text-anchor: middle; dominant-baseline: central; }
28525
+ .sx-punnett-corner { fill: #f1f5f9; stroke: #94a3b8; stroke-width: 1; }
28526
+ .sx-punnett-cornerline { stroke: #cbd5e1; stroke-width: 1; }
28527
+ .sx-punnett-corner-p1 { font: 600 11px sans-serif; fill: #1e3a8a; dominant-baseline: hanging; }
28528
+ .sx-punnett-corner-p2 { font: 600 11px sans-serif; fill: #1e3a8a; }
28529
+ .sx-punnett-header { fill: #e2e8f0; stroke: #94a3b8; stroke-width: 1; }
28530
+ .sx-punnett-gamete { font: 700 14px ui-monospace, "SF Mono", Menlo, monospace; fill: #0f172a; text-anchor: middle; dominant-baseline: central; }
28531
+ .sx-punnett-cell { stroke: #94a3b8; stroke-width: 1; }
28532
+ .sx-punnett-genotype { font: 700 15px ui-monospace, "SF Mono", Menlo, monospace; fill: #0f172a; text-anchor: middle; dominant-baseline: central; }
28533
+ .sx-punnett-footer-head { font: 700 13px sans-serif; fill: #111; }
28534
+ .sx-punnett-legend { font: 500 12.5px sans-serif; fill: #1f2937; dominant-baseline: central; }
28535
+ .sx-punnett-geno-ratio { font: 500 12px sans-serif; fill: #475569; }
28536
+ .sx-punnett-hint { font: 500 13px sans-serif; fill: #64748b; text-anchor: middle; }
27781
28537
  `.trim();
27782
28538
  function axisArrow() {
27783
28539
  return el(
@@ -28437,8 +29193,8 @@ function renderPoints(ast, lay) {
28437
29193
  }
28438
29194
  return group({ id: "sx-matrix-points" }, nodes);
28439
29195
  }
28440
- function renderOnePoint(pl, categories) {
28441
- const p = pl.point;
29196
+ function renderOnePoint(pl2, categories) {
29197
+ const p = pl2.point;
28442
29198
  const color = bubbleFill(p, categories);
28443
29199
  const shape = p.shape ?? "circle";
28444
29200
  let shapeEl;
@@ -28447,9 +29203,9 @@ function renderOnePoint(pl, categories) {
28447
29203
  const fillOpacity = p.size !== void 0 ? 0.45 : 0.75;
28448
29204
  if (shape === "circle") {
28449
29205
  shapeEl = circle({
28450
- cx: pl.px,
28451
- cy: pl.py,
28452
- r: pl.r,
29206
+ cx: pl2.px,
29207
+ cy: pl2.py,
29208
+ r: pl2.r,
28453
29209
  fill: color,
28454
29210
  "fill-opacity": fillOpacity,
28455
29211
  stroke,
@@ -28458,10 +29214,10 @@ function renderOnePoint(pl, categories) {
28458
29214
  });
28459
29215
  } else if (shape === "square") {
28460
29216
  shapeEl = rect({
28461
- x: pl.px - pl.r,
28462
- y: pl.py - pl.r,
28463
- width: pl.r * 2,
28464
- height: pl.r * 2,
29217
+ x: pl2.px - pl2.r,
29218
+ y: pl2.py - pl2.r,
29219
+ width: pl2.r * 2,
29220
+ height: pl2.r * 2,
28465
29221
  fill: color,
28466
29222
  "fill-opacity": fillOpacity,
28467
29223
  stroke,
@@ -28469,9 +29225,9 @@ function renderOnePoint(pl, categories) {
28469
29225
  class: "sx-matrix-bubble"
28470
29226
  });
28471
29227
  } else if (shape === "diamond") {
28472
- const r6 = pl.r;
29228
+ const r6 = pl2.r;
28473
29229
  shapeEl = polygon({
28474
- points: `${pl.px},${pl.py - r6} ${pl.px + r6},${pl.py} ${pl.px},${pl.py + r6} ${pl.px - r6},${pl.py}`,
29230
+ points: `${pl2.px},${pl2.py - r6} ${pl2.px + r6},${pl2.py} ${pl2.px},${pl2.py + r6} ${pl2.px - r6},${pl2.py}`,
28475
29231
  fill: color,
28476
29232
  "fill-opacity": fillOpacity,
28477
29233
  stroke,
@@ -28479,9 +29235,9 @@ function renderOnePoint(pl, categories) {
28479
29235
  class: "sx-matrix-bubble"
28480
29236
  });
28481
29237
  } else {
28482
- const r6 = pl.r;
29238
+ const r6 = pl2.r;
28483
29239
  shapeEl = polygon({
28484
- points: `${pl.px},${pl.py - r6} ${pl.px + r6},${pl.py + r6 * 0.8} ${pl.px - r6},${pl.py + r6 * 0.8}`,
29240
+ points: `${pl2.px},${pl2.py - r6} ${pl2.px + r6},${pl2.py + r6 * 0.8} ${pl2.px - r6},${pl2.py + r6 * 0.8}`,
28485
29241
  fill: color,
28486
29242
  "fill-opacity": fillOpacity,
28487
29243
  stroke,
@@ -28489,23 +29245,23 @@ function renderOnePoint(pl, categories) {
28489
29245
  class: "sx-matrix-bubble"
28490
29246
  });
28491
29247
  }
28492
- const leader = pl.label.external ? line({
28493
- x1: pl.px,
28494
- y1: pl.py,
28495
- x2: pl.label.lx,
28496
- y2: pl.label.ly,
29248
+ const leader = pl2.label.external ? line({
29249
+ x1: pl2.px,
29250
+ y1: pl2.py,
29251
+ x2: pl2.label.lx,
29252
+ y2: pl2.label.ly,
28497
29253
  class: "sx-matrix-leader"
28498
29254
  }) : "";
28499
29255
  const label = text(
28500
- { x: pl.label.lx, y: pl.label.ly, class: "sx-matrix-label" },
28501
- pl.label.text
29256
+ { x: pl2.label.lx, y: pl2.label.ly, class: "sx-matrix-label" },
29257
+ pl2.label.text
28502
29258
  );
28503
29259
  let badge = "";
28504
29260
  if (p.offChart) {
28505
- const bx = pl.px;
28506
- const by = pl.py;
29261
+ const bx = pl2.px;
29262
+ const by = pl2.py;
28507
29263
  badge = text(
28508
- { x: bx + pl.r + 4, y: by - pl.r - 2, class: "sx-matrix-offchart", "font-size": 14, "font-weight": 700 },
29264
+ { x: bx + pl2.r + 4, y: by - pl2.r - 2, class: "sx-matrix-offchart", "font-size": 14, "font-weight": 700 },
28509
29265
  "\u2197"
28510
29266
  );
28511
29267
  }
@@ -28660,7 +29416,7 @@ function renderSipocAST(ast) {
28660
29416
  desc(
28661
29417
  `SIPOC scoping table \u2014 ${sipoc.suppliers.length} supplier(s), ${sipoc.inputs.length} input(s), ${sipoc.process.length} process step(s), ${sipoc.outputs.length} output(s), ${sipoc.customers.length} customer(s)`
28662
29418
  ),
28663
- defs([el("style", {}, CSS)]),
29419
+ defs([el("style", {}, CSS2)]),
28664
29420
  ...nodes
28665
29421
  ]
28666
29422
  );
@@ -28889,14 +29645,135 @@ function renderQfdAST(ast) {
28889
29645
  desc(
28890
29646
  `QFD House of Quality \u2014 ${qfd.whats.length} customer requirement(s), ${qfd.hows.length} engineering characteristic(s), ${qfd.relationships.length} relationship(s); technical importance computed per column`
28891
29647
  ),
28892
- defs([el("style", {}, CSS)]),
29648
+ defs([el("style", {}, CSS2)]),
28893
29649
  ...nodes
28894
29650
  ]
28895
29651
  );
28896
29652
  }
29653
+ var PUNNETT_TINTS = ["#dbeafe", "#dcfce7", "#fef9c3", "#fed7aa", "#fae8ff", "#cffafe", "#fee2e2", "#e0e7ff"];
29654
+ var PUNNETT_STRONG = ["#2563eb", "#16a34a", "#ca8a04", "#ea580c", "#9333ea", "#0891b2", "#dc2626", "#4f46e5"];
29655
+ function genotypeText(parent, genes) {
29656
+ return parent.map((pair, i) => {
29657
+ const dom = genes[i]?.dominant;
29658
+ const [a, b] = [pair[0] ?? "", pair[1] ?? ""];
29659
+ return a === dom ? a + b : b === dom ? b + a : a + b;
29660
+ }).join("");
29661
+ }
29662
+ function renderPunnettAST(ast) {
29663
+ const pd = ast.punnett;
29664
+ const lay = layoutPunnett(ast);
29665
+ const svgWrap = (body, descText2) => svgRoot(
29666
+ {
29667
+ class: "sx-matrix sx-punnett",
29668
+ "data-diagram-type": "matrix",
29669
+ "data-mode": "punnett",
29670
+ width: lay.canvasWidth,
29671
+ height: lay.canvasHeight,
29672
+ viewBox: `0 0 ${lay.canvasWidth} ${lay.canvasHeight}`,
29673
+ role: "graphics-document"
29674
+ },
29675
+ [
29676
+ title(ast.title ? `Punnett square \u2014 ${escapeXml(ast.title)}` : "Punnett square"),
29677
+ desc(descText2),
29678
+ defs([el("style", {}, CSS2)]),
29679
+ ...body
29680
+ ]
29681
+ );
29682
+ if (!pd || pd.genes.length === 0) {
29683
+ return svgWrap(
29684
+ [
29685
+ text(
29686
+ { x: lay.canvasWidth / 2, y: 44, class: "sx-matrix-title", "text-anchor": "middle" },
29687
+ ast.title ?? "Punnett square"
29688
+ ),
29689
+ text({ x: lay.canvasWidth / 2, y: 84, class: "sx-punnett-hint" }, "Add a cross, e.g. cross: Bb x Bb")
29690
+ ],
29691
+ "Empty Punnett square \u2014 no cross specified"
29692
+ );
29693
+ }
29694
+ const result = computePunnett(pd);
29695
+ const phenoColor = /* @__PURE__ */ new Map();
29696
+ result.phenotypeRatio.forEach((p, i) => {
29697
+ phenoColor.set(p.key, {
29698
+ tint: PUNNETT_TINTS[i % PUNNETT_TINTS.length],
29699
+ strong: PUNNETT_STRONG[i % PUNNETT_STRONG.length]
29700
+ });
29701
+ });
29702
+ const nodes = [];
29703
+ if (ast.title) {
29704
+ nodes.push(
29705
+ text(
29706
+ { x: lay.canvasWidth / 2, y: 24, class: "sx-matrix-title", "text-anchor": "middle" },
29707
+ ast.title
29708
+ )
29709
+ );
29710
+ }
29711
+ const gx0 = lay.x0 + lay.headerW;
29712
+ const gy0 = lay.y0 + lay.headerH;
29713
+ nodes.push(rect({ x: lay.x0, y: lay.y0, width: lay.headerW, height: lay.headerH, class: "sx-punnett-corner" }));
29714
+ nodes.push(
29715
+ line({ x1: lay.x0, y1: lay.y0, x2: lay.x0 + lay.headerW, y2: lay.y0 + lay.headerH, class: "sx-punnett-cornerline" })
29716
+ );
29717
+ nodes.push(
29718
+ text(
29719
+ { x: lay.x0 + lay.headerW - 6, y: lay.y0 + 8, class: "sx-punnett-corner-p1", "text-anchor": "end" },
29720
+ genotypeText(pd.parent1, pd.genes)
29721
+ )
29722
+ );
29723
+ nodes.push(
29724
+ text(
29725
+ { x: lay.x0 + 6, y: lay.y0 + lay.headerH - 8, class: "sx-punnett-corner-p2", "text-anchor": "start" },
29726
+ genotypeText(pd.parent2, pd.genes)
29727
+ )
29728
+ );
29729
+ result.gametes1.forEach((g, c) => {
29730
+ const cx = gx0 + c * lay.cellW;
29731
+ nodes.push(rect({ x: cx, y: lay.y0, width: lay.cellW, height: lay.headerH, class: "sx-punnett-header" }));
29732
+ nodes.push(text({ x: cx + lay.cellW / 2, y: lay.y0 + lay.headerH / 2, class: "sx-punnett-gamete" }, g));
29733
+ });
29734
+ result.gametes2.forEach((g, r6) => {
29735
+ const cy = gy0 + r6 * lay.cellH;
29736
+ nodes.push(rect({ x: lay.x0, y: cy, width: lay.headerW, height: lay.cellH, class: "sx-punnett-header" }));
29737
+ nodes.push(text({ x: lay.x0 + lay.headerW / 2, y: cy + lay.cellH / 2, class: "sx-punnett-gamete" }, g));
29738
+ });
29739
+ for (let r6 = 0; r6 < result.grid.length; r6++) {
29740
+ const row = result.grid[r6];
29741
+ for (let c = 0; c < row.length; c++) {
29742
+ const cell = row[c];
29743
+ const color = phenoColor.get(cell.phenotypeKey);
29744
+ const cx = gx0 + c * lay.cellW;
29745
+ const cy = gy0 + r6 * lay.cellH;
29746
+ nodes.push(rect({ x: cx, y: cy, width: lay.cellW, height: lay.cellH, fill: color.tint, class: "sx-punnett-cell" }));
29747
+ nodes.push(text({ x: cx + lay.cellW / 2, y: cy + lay.cellH / 2, class: "sx-punnett-genotype" }, cell.genotype));
29748
+ }
29749
+ }
29750
+ const footer = punnettFooter(result);
29751
+ let fy = lay.footerY + 18;
29752
+ nodes.push(
29753
+ text({ x: lay.x0, y: fy, class: "sx-punnett-footer-head" }, `Phenotype ratio ${footer.phenotypeRatio}`)
29754
+ );
29755
+ fy += 22;
29756
+ for (const p of footer.legend) {
29757
+ const color = phenoColor.get(p.key);
29758
+ nodes.push(
29759
+ rect({ x: lay.x0, y: fy - 8, width: 14, height: 14, fill: color.tint, stroke: color.strong, "stroke-width": 1.4 })
29760
+ );
29761
+ nodes.push(text({ x: lay.x0 + 22, y: fy, class: "sx-punnett-legend" }, `${p.count} \xD7 ${p.label}`));
29762
+ fy += 22;
29763
+ }
29764
+ nodes.push(
29765
+ text(
29766
+ { x: lay.x0, y: fy + 4, class: "sx-punnett-geno-ratio" },
29767
+ `Genotype ratio ${footer.genotypeRatio} \u2014 ${footer.genotypeDetail}`
29768
+ )
29769
+ );
29770
+ const descText = `Punnett square \u2014 ${pd.genes.length === 1 ? "monohybrid" : pd.genes.length === 2 ? "dihybrid" : `${pd.genes.length}-gene`} cross ${genotypeText(pd.parent1, pd.genes)} \xD7 ${genotypeText(pd.parent2, pd.genes)}; phenotype ratio ${footer.phenotypeRatio}`;
29771
+ return svgWrap(nodes, descText);
29772
+ }
28897
29773
  function renderMatrixAST(ast) {
28898
29774
  if (ast.mode === "sipoc") return renderSipocAST(ast);
28899
29775
  if (ast.mode === "qfd") return renderQfdAST(ast);
29776
+ if (ast.mode === "punnett") return renderPunnettAST(ast);
28900
29777
  const lay = layoutMatrix(ast);
28901
29778
  const needsLegendSpace = lay.categories.length > 0 || ast.mode === "correlation";
28902
29779
  const extraWidth = needsLegendSpace && lay.plot.x0 + lay.plot.w + 140 > lay.canvasWidth ? 160 : 0;
@@ -28930,7 +29807,7 @@ function renderMatrixAST(ast) {
28930
29807
  desc(
28931
29808
  `Matrix diagram${ast.template ? ` (${ast.template} template)` : ""}, ${ast.mode} mode, ${ast.points.length} point(s)`
28932
29809
  ),
28933
- defs([el("style", {}, CSS), axisArrow()]),
29810
+ defs([el("style", {}, CSS2), axisArrow()]),
28934
29811
  ...body
28935
29812
  ]
28936
29813
  );
@@ -28962,7 +29839,7 @@ var ErdParseError = class extends Error {
28962
29839
  }
28963
29840
  lineNumber;
28964
29841
  };
28965
- function stripComment13(s) {
29842
+ function stripComment14(s) {
28966
29843
  let out = "";
28967
29844
  let inQuote = false;
28968
29845
  for (let i = 0; i < s.length; i++) {
@@ -29016,7 +29893,7 @@ function parseCardToken(raw, side) {
29016
29893
  }
29017
29894
  function lex(text2) {
29018
29895
  return text2.split(/\r?\n/).map((raw, i) => ({
29019
- text: stripComment13(raw).trim(),
29896
+ text: stripComment14(raw).trim(),
29020
29897
  lineNumber: i + 1
29021
29898
  })).filter((l) => l.text.length > 0);
29022
29899
  }
@@ -30143,7 +31020,7 @@ var BreadboardParseError = class extends Error {
30143
31020
  }
30144
31021
  lineNumber;
30145
31022
  };
30146
- function stripComment14(s) {
31023
+ function stripComment15(s) {
30147
31024
  let out = "";
30148
31025
  let inQuote = false;
30149
31026
  for (let i = 0; i < s.length; i++) {
@@ -30164,7 +31041,7 @@ function unquote8(v) {
30164
31041
  }
30165
31042
  function lex2(text2) {
30166
31043
  return text2.split(/\r?\n/).map((raw, i) => ({
30167
- text: stripComment14(raw).trimEnd().replace(/^\s+/, ""),
31044
+ text: stripComment15(raw).trimEnd().replace(/^\s+/, ""),
30168
31045
  lineNumber: i + 1
30169
31046
  })).filter((l) => l.text.length > 0);
30170
31047
  }
@@ -30505,9 +31382,9 @@ function resistorBands(value) {
30505
31382
  const s = String(value).replace(/[^\d.kKmMrRΩ]/g, "");
30506
31383
  const m = s.match(/^([\d.]+)\s*([kKmM]?)/);
30507
31384
  if (!m) return ["#92400e", "#92400e", "#1f2937", "#facc15"];
30508
- const num3 = parseFloat(m[1]);
31385
+ const num4 = parseFloat(m[1]);
30509
31386
  const mult = m[2] === "k" || m[2] === "K" ? 1e3 : m[2] === "m" || m[2] === "M" ? 1e6 : 1;
30510
- const ohms = Math.round(num3 * mult);
31387
+ const ohms = Math.round(num4 * mult);
30511
31388
  if (!Number.isFinite(ohms) || ohms <= 0) return ["#92400e", "#92400e", "#1f2937", "#facc15"];
30512
31389
  const str = String(ohms);
30513
31390
  if (str.length <= 1) return [BAND_COLORS["0"], BAND_COLORS[str[0]], BAND_COLORS["0"], "#facc15"];
@@ -32178,7 +33055,7 @@ function renderBpmnLayout(layout, _config) {
32178
33055
  out.push(title(ast.title ?? "BPMN diagram"));
32179
33056
  out.push(desc(`BPMN ${ast.direction} \u2014 ${ast.pools.length} pool(s), ${layout.objects.length} flow object(s).`));
32180
33057
  out.push(buildDefs());
32181
- for (const pl of layout.pools) out.push(renderPool(pl));
33058
+ for (const pl2 of layout.pools) out.push(renderPool(pl2));
32182
33059
  for (const lan of layout.lanes) {
32183
33060
  out.push(
32184
33061
  group({ class: "schematex-bpmn-lane" }, [
@@ -32288,25 +33165,25 @@ function buildDefs() {
32288
33165
  )
32289
33166
  ]);
32290
33167
  }
32291
- function renderPool(pl) {
32292
- const labelCx = pl.labelX + pl.labelWidth / 2;
32293
- const labelCy = pl.labelY + pl.height / 2;
32294
- if (pl.pool.blackbox) {
33168
+ function renderPool(pl2) {
33169
+ const labelCx = pl2.labelX + pl2.labelWidth / 2;
33170
+ const labelCy = pl2.labelY + pl2.height / 2;
33171
+ if (pl2.pool.blackbox) {
32295
33172
  return group({ class: "schematex-bpmn-pool blackbox" }, [
32296
33173
  rect({
32297
- x: pl.x,
32298
- y: pl.y,
32299
- width: pl.width,
32300
- height: pl.height,
33174
+ x: pl2.x,
33175
+ y: pl2.y,
33176
+ width: pl2.width,
33177
+ height: pl2.height,
32301
33178
  fill: POOL_FILL,
32302
33179
  stroke: STROKE,
32303
33180
  "stroke-width": 1.5
32304
33181
  }),
32305
33182
  rect({
32306
- x: pl.x,
32307
- y: pl.y,
32308
- width: pl.labelWidth,
32309
- height: pl.height,
33183
+ x: pl2.x,
33184
+ y: pl2.y,
33185
+ width: pl2.labelWidth,
33186
+ height: pl2.height,
32310
33187
  fill: LABEL_BAND_FILL,
32311
33188
  stroke: STROKE,
32312
33189
  "stroke-width": 1
@@ -32323,25 +33200,25 @@ function renderPool(pl) {
32323
33200
  "font-weight": "bold",
32324
33201
  fill: STROKE
32325
33202
  },
32326
- pl.pool.label
33203
+ pl2.pool.label
32327
33204
  )
32328
33205
  ]);
32329
33206
  }
32330
33207
  return group({ class: "schematex-bpmn-pool" }, [
32331
33208
  rect({
32332
- x: pl.x,
32333
- y: pl.y,
32334
- width: pl.width,
32335
- height: pl.height,
33209
+ x: pl2.x,
33210
+ y: pl2.y,
33211
+ width: pl2.width,
33212
+ height: pl2.height,
32336
33213
  fill: POOL_FILL,
32337
33214
  stroke: STROKE,
32338
33215
  "stroke-width": 1.5
32339
33216
  }),
32340
33217
  rect({
32341
- x: pl.x,
32342
- y: pl.y,
32343
- width: pl.labelWidth,
32344
- height: pl.height,
33218
+ x: pl2.x,
33219
+ y: pl2.y,
33220
+ width: pl2.labelWidth,
33221
+ height: pl2.height,
32345
33222
  fill: LABEL_BAND_FILL,
32346
33223
  stroke: STROKE,
32347
33224
  "stroke-width": 1
@@ -32358,7 +33235,7 @@ function renderPool(pl) {
32358
33235
  "font-weight": "bold",
32359
33236
  fill: STROKE
32360
33237
  },
32361
- pl.pool.label
33238
+ pl2.pool.label
32362
33239
  )
32363
33240
  ]);
32364
33241
  }
@@ -35090,7 +35967,8 @@ var plugins = [
35090
35967
  gitgraph,
35091
35968
  epc,
35092
35969
  idef0,
35093
- threatmodel
35970
+ threatmodel,
35971
+ welding
35094
35972
  ];
35095
35973
  function detectPlugin(text2, config) {
35096
35974
  if (config?.type) {
@@ -35238,6 +36116,6 @@ function renderWithPlugin(prepared, plugin, config) {
35238
36116
  return plugin.render(prepared, renderConfig);
35239
36117
  }
35240
36118
 
35241
- export { GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase };
35242
- //# sourceMappingURL=chunk-XI5QP7LM.js.map
35243
- //# sourceMappingURL=chunk-XI5QP7LM.js.map
36119
+ export { GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
36120
+ //# sourceMappingURL=chunk-J6AIKEVV.js.map
36121
+ //# sourceMappingURL=chunk-J6AIKEVV.js.map