schematex 0.6.10 → 0.7.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 (35) hide show
  1. package/dist/ai/ai-sdk.cjs +8 -8
  2. package/dist/ai/ai-sdk.d.cts +1 -1
  3. package/dist/ai/ai-sdk.d.ts +1 -1
  4. package/dist/ai/ai-sdk.js +3 -3
  5. package/dist/ai/index.cjs +14 -14
  6. package/dist/ai/index.js +3 -3
  7. package/dist/browser.cjs +9 -9
  8. package/dist/browser.js +3 -3
  9. package/dist/{chunk-DZGA25O5.cjs → chunk-CTMJ3XP2.cjs} +156 -9
  10. package/dist/chunk-CTMJ3XP2.cjs.map +1 -0
  11. package/dist/{chunk-HL5PS6MG.js → chunk-EENA7KNU.js} +324 -27
  12. package/dist/chunk-EENA7KNU.js.map +1 -0
  13. package/dist/{chunk-3YRYBTLG.cjs → chunk-HFATQXFN.cjs} +1401 -148
  14. package/dist/chunk-HFATQXFN.cjs.map +1 -0
  15. package/dist/{chunk-6AWASOFO.js → chunk-IFNNV54X.js} +1400 -147
  16. package/dist/chunk-IFNNV54X.js.map +1 -0
  17. package/dist/{chunk-X4P4HKHP.js → chunk-IFXHIYZE.js} +154 -7
  18. package/dist/chunk-IFXHIYZE.js.map +1 -0
  19. package/dist/{chunk-BL57NQKN.cjs → chunk-JAYJ2G4R.cjs} +324 -27
  20. package/dist/chunk-JAYJ2G4R.cjs.map +1 -0
  21. package/dist/diagrams/phylo/index.cjs +6 -6
  22. package/dist/diagrams/phylo/index.d.cts +21 -0
  23. package/dist/diagrams/phylo/index.d.ts +21 -0
  24. package/dist/diagrams/phylo/index.js +1 -1
  25. package/dist/index.cjs +25 -25
  26. package/dist/index.js +3 -3
  27. package/dist/react.cjs +3 -3
  28. package/dist/react.js +2 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-3YRYBTLG.cjs.map +0 -1
  31. package/dist/chunk-6AWASOFO.js.map +0 -1
  32. package/dist/chunk-BL57NQKN.cjs.map +0 -1
  33. package/dist/chunk-DZGA25O5.cjs.map +0 -1
  34. package/dist/chunk-HL5PS6MG.js.map +0 -1
  35. package/dist/chunk-X4P4HKHP.js.map +0 -1
@@ -11,7 +11,7 @@ import { genogram } from './chunk-6NUAGU6O.js';
11
11
  import { ecomap } from './chunk-K2D6VFLP.js';
12
12
  import { pedigree } from './chunk-JN6FHUC6.js';
13
13
  import { parseFrontmatter } from './chunk-2KTQ75LN.js';
14
- import { phylo } from './chunk-HL5PS6MG.js';
14
+ import { phylo } from './chunk-EENA7KNU.js';
15
15
  import { sociogram } from './chunk-OFKRELZK.js';
16
16
  import { timing } from './chunk-P26FCZP3.js';
17
17
  import { logic } from './chunk-3GAPHXCE.js';
@@ -19,6 +19,207 @@ import { resolveBaseTheme, resolveTimelineTheme, cssCustomProperties, resolvePet
19
19
  import { matchQuotedTitle, stripQuotes } from './chunk-5IKOLUWK.js';
20
20
  import { el, escapeXml, group, rect, text, line, path, circle, polygon, title, desc, svgRoot, defs, multilineText } from './chunk-SYYBKDL7.js';
21
21
 
22
+ // src/diagrams/decisiontree/influence-parser.ts
23
+ function preprocess(src) {
24
+ const out = [];
25
+ const lines = src.split(/\r?\n/);
26
+ for (let i = 0; i < lines.length; i++) {
27
+ const raw = lines[i];
28
+ if (raw === void 0) continue;
29
+ const trimmed = raw.trim();
30
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
31
+ out.push({ text: trimmed, line: i + 1 });
32
+ }
33
+ return out;
34
+ }
35
+ function tokenize(s, lineNum) {
36
+ const tokens = [];
37
+ let i = 0;
38
+ while (i < s.length) {
39
+ const ch = s[i];
40
+ if (/\s/.test(ch)) {
41
+ i++;
42
+ continue;
43
+ }
44
+ if (ch === '"') {
45
+ const end = s.indexOf('"', i + 1);
46
+ if (end < 0) throw new DTreeParseError(`Unterminated string: ${s}`, lineNum);
47
+ tokens.push(s.slice(i, end + 1));
48
+ i = end + 1;
49
+ continue;
50
+ }
51
+ let j = i;
52
+ while (j < s.length && !/\s/.test(s[j]) && s[j] !== '"') j++;
53
+ tokens.push(s.slice(i, j));
54
+ i = j;
55
+ }
56
+ return tokens;
57
+ }
58
+ function unquote(s) {
59
+ if (s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1);
60
+ return s;
61
+ }
62
+ function isQuoted(s) {
63
+ return s.startsWith('"') && s.endsWith('"') && s.length >= 2;
64
+ }
65
+ function toKind(kw) {
66
+ if (kw === "decision" || kw === "dec") return "decision";
67
+ if (kw === "chance" || kw === "uncertainty" || kw === "event") return "chance";
68
+ if (kw === "value" || kw === "utility" || kw === "objective") return "value";
69
+ return void 0;
70
+ }
71
+ var NODE_KEYWORDS = /* @__PURE__ */ new Set([
72
+ "decision",
73
+ "dec",
74
+ "chance",
75
+ "uncertainty",
76
+ "event",
77
+ "value",
78
+ "utility",
79
+ "objective",
80
+ "node"
81
+ ]);
82
+ function parseInfluence(src, title2) {
83
+ const lines = preprocess(src);
84
+ const nodes = [];
85
+ const nodeById = /* @__PURE__ */ new Map();
86
+ const arcDecls = [];
87
+ let direction = "left-right";
88
+ for (const { text: text2, line: line2 } of lines) {
89
+ const cfg = text2.match(/^([a-zA-Z][\w-]*)\s*:\s*(.+)$/);
90
+ if (cfg && !text2.includes("->") && !NODE_KEYWORDS.has(cfg[1].toLowerCase())) {
91
+ const key = cfg[1].toLowerCase();
92
+ const val = cfg[2].trim().toLowerCase();
93
+ if (key === "direction") {
94
+ direction = val === "top-down" || val === "td" ? "top-down" : "left-right";
95
+ }
96
+ continue;
97
+ }
98
+ if (text2.includes("->")) {
99
+ const arrowIdx = text2.indexOf("->");
100
+ const fromPart = text2.slice(0, arrowIdx).trim();
101
+ const afterTokens = tokenize(text2.slice(arrowIdx + 2).trim(), line2);
102
+ const to = afterTokens[0];
103
+ if (!fromPart || !to) {
104
+ throw new DTreeParseError(`Malformed arc: "${text2}"`, line2);
105
+ }
106
+ let label2;
107
+ if (afterTokens[1] !== void 0 && isQuoted(afterTokens[1])) {
108
+ label2 = unquote(afterTokens[1]);
109
+ }
110
+ arcDecls.push({ from: fromPart, to: unquote(to), label: label2, line: line2 });
111
+ continue;
112
+ }
113
+ const tokens = tokenize(text2, line2);
114
+ const kw = tokens[0];
115
+ if (!kw) continue;
116
+ let kind;
117
+ let idIdx = 1;
118
+ if (kw === "node") {
119
+ kind = void 0;
120
+ } else {
121
+ kind = toKind(kw.toLowerCase());
122
+ if (!kind) {
123
+ throw new DTreeParseError(
124
+ `Unknown influence statement "${kw}" (expected decision/chance/value or an arc "a -> b")`,
125
+ line2
126
+ );
127
+ }
128
+ }
129
+ const id = tokens[idIdx];
130
+ if (!id || isQuoted(id)) {
131
+ throw new DTreeParseError(`Node declaration requires an id: "${text2}"`, line2);
132
+ }
133
+ idIdx++;
134
+ let label = "";
135
+ let utility;
136
+ for (let k = idIdx; k < tokens.length; k++) {
137
+ const tok = tokens[k];
138
+ if (isQuoted(tok)) {
139
+ label = unquote(tok);
140
+ } else if (tok.includes("=")) {
141
+ const eq = tok.indexOf("=");
142
+ const key = tok.slice(0, eq).toLowerCase();
143
+ const value = tok.slice(eq + 1);
144
+ if (key === "utility" || key === "payoff" || key === "value") {
145
+ const num = Number(value);
146
+ if (!Number.isNaN(num)) utility = num;
147
+ } else if (key === "kind") {
148
+ const resolved = toKind(value.toLowerCase());
149
+ if (!resolved) throw new DTreeParseError(`Unknown node kind "${value}"`, line2);
150
+ kind = resolved;
151
+ }
152
+ }
153
+ }
154
+ if (!kind) {
155
+ throw new DTreeParseError(`node "${id}" is missing a kind (use kind=decision|chance|value)`, line2);
156
+ }
157
+ if (nodeById.has(id)) {
158
+ throw new DTreeParseError(`Duplicate node id "${id}"`, line2);
159
+ }
160
+ const node = { id, kind, label: label || id };
161
+ if (utility !== void 0) node.utility = utility;
162
+ nodes.push(node);
163
+ nodeById.set(id, node);
164
+ }
165
+ if (nodes.length === 0) {
166
+ throw new DTreeParseError("Influence diagram has no nodes");
167
+ }
168
+ const valueNodes = nodes.filter((n) => n.kind === "value");
169
+ if (valueNodes.length === 0) {
170
+ throw new DTreeParseError(
171
+ 'Influence diagram requires at least one value node (declare `value <id> "..."`)'
172
+ );
173
+ }
174
+ const arcs = [];
175
+ for (const a of arcDecls) {
176
+ const fromNode = nodeById.get(a.from);
177
+ const toNode = nodeById.get(a.to);
178
+ if (!fromNode) throw new DTreeParseError(`Arc references undefined node "${a.from}"`, a.line);
179
+ if (!toNode) throw new DTreeParseError(`Arc references undefined node "${a.to}"`, a.line);
180
+ if (a.from === a.to) throw new DTreeParseError(`Self-loop on node "${a.from}" is not allowed`, a.line);
181
+ const kind = toNode.kind === "decision" ? "information" : toNode.kind === "value" ? "functional" : "relevance";
182
+ const arc = { from: a.from, to: a.to, kind };
183
+ if (a.label !== void 0) arc.label = a.label;
184
+ arcs.push(arc);
185
+ }
186
+ detectCycle(nodes, arcs);
187
+ return {
188
+ type: "decisiontree",
189
+ mode: "influence",
190
+ title: title2,
191
+ direction,
192
+ nodes,
193
+ arcs
194
+ };
195
+ }
196
+ function detectCycle(nodes, arcs) {
197
+ const adj = /* @__PURE__ */ new Map();
198
+ for (const n of nodes) adj.set(n.id, []);
199
+ for (const a of arcs) adj.get(a.from).push(a.to);
200
+ const state2 = /* @__PURE__ */ new Map();
201
+ for (const n of nodes) state2.set(n.id, 0);
202
+ const stackPath = [];
203
+ function visit(id) {
204
+ state2.set(id, 1);
205
+ stackPath.push(id);
206
+ for (const next of adj.get(id) ?? []) {
207
+ const s = state2.get(next) ?? 0;
208
+ if (s === 1) {
209
+ const cycleStart = stackPath.indexOf(next);
210
+ const cycle = [...stackPath.slice(cycleStart), next].join(" -> ");
211
+ throw new DTreeParseError(`Influence diagram must be acyclic \u2014 cycle detected: ${cycle}`);
212
+ }
213
+ if (s === 0) visit(next);
214
+ }
215
+ stackPath.pop();
216
+ state2.set(id, 2);
217
+ }
218
+ for (const n of nodes) {
219
+ if (state2.get(n.id) === 0) visit(n.id);
220
+ }
221
+ }
222
+
22
223
  // src/diagrams/decisiontree/parser.ts
23
224
  var DTreeParseError = class extends Error {
24
225
  constructor(message, line2, column, source) {
@@ -32,7 +233,7 @@ var DTreeParseError = class extends Error {
32
233
  column;
33
234
  source;
34
235
  };
35
- function preprocess(src) {
236
+ function preprocess2(src) {
36
237
  const out = [];
37
238
  const lines = src.split(/\r?\n/);
38
239
  for (let i = 0; i < lines.length; i++) {
@@ -44,7 +245,7 @@ function preprocess(src) {
44
245
  }
45
246
  return out;
46
247
  }
47
- function tokenize(s) {
248
+ function tokenize2(s) {
48
249
  const tokens = [];
49
250
  let i = 0;
50
251
  while (i < s.length) {
@@ -75,7 +276,7 @@ function tokenize(s) {
75
276
  }
76
277
  return tokens;
77
278
  }
78
- function unquote(s) {
279
+ function unquote2(s) {
79
280
  if (s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1);
80
281
  return s;
81
282
  }
@@ -85,7 +286,7 @@ function parseKV(tokens) {
85
286
  const rest = [];
86
287
  for (const tok of tokens) {
87
288
  if (tok.startsWith('"') && tok.endsWith('"')) {
88
- labels.push(unquote(tok));
289
+ labels.push(unquote2(tok));
89
290
  } else if (tok.includes("=")) {
90
291
  const idx = tok.indexOf("=");
91
292
  const k = tok.slice(0, idx);
@@ -114,7 +315,7 @@ function parseDecisionLine(tokens, _ctx, lineNum) {
114
315
  idx++;
115
316
  const next = tokens[idx];
116
317
  if (!next || !next.startsWith('"')) throw new DTreeParseError(`"choice" requires a label`, lineNum);
117
- incomingChoice = unquote(next);
318
+ incomingChoice = unquote2(next);
118
319
  idx++;
119
320
  if (idx >= tokens.length) {
120
321
  return {
@@ -213,7 +414,7 @@ function parseTaxonomyLine(tokens, _ctx, lineNum) {
213
414
  idx++;
214
415
  const lbl = tokens[idx];
215
416
  if (!lbl || !lbl.startsWith('"')) throw new DTreeParseError(`"label" requires a quoted string`, lineNum);
216
- branchLabel = unquote(lbl.replace(/":?$/, '"'));
417
+ branchLabel = unquote2(lbl.replace(/":?$/, '"'));
217
418
  if (lbl.endsWith(':"')) ;
218
419
  idx++;
219
420
  if (tokens[idx] === ":") idx++;
@@ -237,7 +438,7 @@ function parseTaxonomyLine(tokens, _ctx, lineNum) {
237
438
  }
238
439
  function parseNodeLine(text2, mode, ctx, lineNum) {
239
440
  const normalized = text2.replace(/"\s*:\s*/g, '" : ');
240
- const tokens = tokenize(normalized).filter((t) => t !== ":");
441
+ const tokens = tokenize2(normalized).filter((t) => t !== ":");
241
442
  if (mode === "decision") return parseDecisionLine(tokens, ctx, lineNum);
242
443
  if (mode === "ml") return parseMlLine(tokens, ctx, lineNum);
243
444
  return parseTaxonomyLine(tokens, ctx, lineNum);
@@ -305,14 +506,19 @@ function validateDecision(node, lineMap) {
305
506
  }
306
507
  function parseDecisionTree(src) {
307
508
  idCounter = 0;
308
- const lines = preprocess(src);
509
+ const lines = preprocess2(src);
309
510
  if (lines.length === 0) throw new DTreeParseError("Empty input");
310
511
  const header = lines.shift();
311
512
  const headerMatch = header.text.match(/^decisiontree(?::(\w+))?(?:\s+"([^"]*)")?\s*$/i);
312
513
  if (!headerMatch) throw new DTreeParseError(`Invalid header: ${header.text}`, header.line);
313
514
  const modeRaw = (headerMatch[1] ?? "taxonomy").toLowerCase();
314
- const mode = modeRaw === "decision" || modeRaw === "da" ? "decision" : modeRaw === "ml" ? "ml" : "taxonomy";
315
515
  const title2 = headerMatch[2];
516
+ const wantsInfluence = modeRaw === "influence" || modeRaw === "id" || lines.some((l) => /^(mode|layout)\s*:\s*influence\s*$/i.test(l.text));
517
+ if (wantsInfluence) {
518
+ const body = lines.map((l) => l.text).join("\n");
519
+ return parseInfluence(body, title2);
520
+ }
521
+ const mode = modeRaw === "decision" || modeRaw === "da" ? "decision" : modeRaw === "ml" ? "ml" : "taxonomy";
316
522
  const nodeKeywords = /* @__PURE__ */ new Set([
317
523
  "decision",
318
524
  "chance",
@@ -623,7 +829,7 @@ function layoutDecisionTree(ast) {
623
829
  enforceSibGap(root, sibH, sibGap);
624
830
  const levelOffsets = computeLevelOffsets(root, sibH, levelGap);
625
831
  setFinal(root, sibH, levelOffsets);
626
- const PADDING3 = 40;
832
+ const PADDING4 = 40;
627
833
  const extraLeft = ast.mode === "decision" && !sibH ? 110 : 0;
628
834
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
629
835
  for (const n of all) {
@@ -648,8 +854,8 @@ function layoutDecisionTree(ast) {
648
854
  maxX = Math.max(maxX, n.xFinal + n.size.w / 2);
649
855
  }
650
856
  }
651
- const offsetX = PADDING3 + extraLeft - minX;
652
- const offsetY = PADDING3 - minY;
857
+ const offsetX = PADDING4 + extraLeft - minX;
858
+ const offsetY = PADDING4 - minY;
653
859
  const layoutNodes = all.map((w) => ({
654
860
  node: w.node,
655
861
  x: w.xFinal + offsetX,
@@ -710,8 +916,8 @@ function layoutDecisionTree(ast) {
710
916
  for (const n of layoutNodes) if (n.node.kind === "end") endX = Math.max(endX, n.x + n.width / 2);
711
917
  payoffColumnX = endX + payoffColGap;
712
918
  }
713
- const width = Math.ceil(maxX - minX + PADDING3 * 2 + extraRight + extraLeft);
714
- const height = Math.ceil(maxY - minY + PADDING3 * 2);
919
+ const width = Math.ceil(maxX - minX + PADDING4 * 2 + extraRight + extraLeft);
920
+ const height = Math.ceil(maxY - minY + PADDING4 * 2);
715
921
  return {
716
922
  width,
717
923
  height,
@@ -741,6 +947,296 @@ function formatProb(p) {
741
947
  return `p=${p}`;
742
948
  }
743
949
 
950
+ // src/diagrams/decisiontree/influence-layout.ts
951
+ function sizeOf2(node) {
952
+ const labelW = Math.min(180, Math.max(72, node.label.length * 8 + 28));
953
+ if (node.kind === "decision") return { w: labelW, h: 48 };
954
+ if (node.kind === "value") return { w: labelW + 12, h: 54 };
955
+ return { w: labelW, h: 52 };
956
+ }
957
+ var LAYER_GAP = 80;
958
+ var CROSS_GAP = 36;
959
+ var PADDING = 36;
960
+ function assignLayers(nodes, arcs) {
961
+ const adj = /* @__PURE__ */ new Map();
962
+ const indeg = /* @__PURE__ */ new Map();
963
+ for (const n of nodes) {
964
+ adj.set(n.id, []);
965
+ indeg.set(n.id, 0);
966
+ }
967
+ for (const a of arcs) {
968
+ adj.get(a.from).push(a.to);
969
+ indeg.set(a.to, (indeg.get(a.to) ?? 0) + 1);
970
+ }
971
+ const queue = [];
972
+ for (const n of nodes) if ((indeg.get(n.id) ?? 0) === 0) queue.push(n.id);
973
+ const layer = /* @__PURE__ */ new Map();
974
+ for (const n of nodes) layer.set(n.id, 0);
975
+ const indegWork = new Map(indeg);
976
+ while (queue.length > 0) {
977
+ const id = queue.shift();
978
+ for (const next of adj.get(id) ?? []) {
979
+ const cand = (layer.get(id) ?? 0) + 1;
980
+ if (cand > (layer.get(next) ?? 0)) layer.set(next, cand);
981
+ indegWork.set(next, (indegWork.get(next) ?? 0) - 1);
982
+ if ((indegWork.get(next) ?? 0) === 0) queue.push(next);
983
+ }
984
+ }
985
+ const maxLayer = Math.max(0, ...nodes.map((n) => layer.get(n.id) ?? 0));
986
+ for (const n of nodes) {
987
+ if (n.kind === "value") layer.set(n.id, maxLayer);
988
+ }
989
+ return layer;
990
+ }
991
+ function layoutInfluence(ast) {
992
+ const flowVertical = ast.direction === "top-down";
993
+ const layerOf = assignLayers(ast.nodes, ast.arcs);
994
+ const sizes = new Map(ast.nodes.map((n) => [n.id, sizeOf2(n)]));
995
+ const layers = /* @__PURE__ */ new Map();
996
+ for (const n of ast.nodes) {
997
+ const l = layerOf.get(n.id) ?? 0;
998
+ if (!layers.has(l)) layers.set(l, []);
999
+ layers.get(l).push(n);
1000
+ }
1001
+ const layerIndices = Array.from(layers.keys()).sort((a, b) => a - b);
1002
+ const flowExtent = (s) => flowVertical ? s.h : s.w;
1003
+ const crossExtent = (s) => flowVertical ? s.w : s.h;
1004
+ const layerFlowSize = /* @__PURE__ */ new Map();
1005
+ for (const l of layerIndices) {
1006
+ let maxF = 0;
1007
+ for (const n of layers.get(l)) maxF = Math.max(maxF, flowExtent(sizes.get(n.id)));
1008
+ layerFlowSize.set(l, maxF);
1009
+ }
1010
+ const layerFlowCentre = /* @__PURE__ */ new Map();
1011
+ let acc = PADDING;
1012
+ for (const l of layerIndices) {
1013
+ const half = layerFlowSize.get(l) / 2;
1014
+ acc += half;
1015
+ layerFlowCentre.set(l, acc);
1016
+ acc += half + LAYER_GAP;
1017
+ }
1018
+ const flowTotal = acc - LAYER_GAP + PADDING;
1019
+ const layerCrossExtent = /* @__PURE__ */ new Map();
1020
+ for (const l of layerIndices) {
1021
+ const ns = layers.get(l);
1022
+ let total = 0;
1023
+ for (let i = 0; i < ns.length; i++) {
1024
+ total += crossExtent(sizes.get(ns[i].id));
1025
+ if (i < ns.length - 1) total += CROSS_GAP;
1026
+ }
1027
+ layerCrossExtent.set(l, total);
1028
+ }
1029
+ const maxCross = Math.max(PADDING, ...Array.from(layerCrossExtent.values()));
1030
+ const crossTotal = maxCross + PADDING * 2;
1031
+ const crossCentre = PADDING + maxCross / 2;
1032
+ const placed = /* @__PURE__ */ new Map();
1033
+ for (const l of layerIndices) {
1034
+ const ns = layers.get(l);
1035
+ const bandLen = layerCrossExtent.get(l);
1036
+ let cursor = crossCentre - bandLen / 2;
1037
+ const flowPos = layerFlowCentre.get(l);
1038
+ for (const n of ns) {
1039
+ const s = sizes.get(n.id);
1040
+ const crossCentrePos = cursor + crossExtent(s) / 2;
1041
+ cursor += crossExtent(s) + CROSS_GAP;
1042
+ const x = flowVertical ? crossCentrePos : flowPos;
1043
+ const y = flowVertical ? flowPos : crossCentrePos;
1044
+ placed.set(n.id, {
1045
+ node: n,
1046
+ x,
1047
+ y,
1048
+ width: s.w,
1049
+ height: s.h,
1050
+ layer: l
1051
+ });
1052
+ }
1053
+ }
1054
+ const width = Math.ceil(flowVertical ? crossTotal : flowTotal);
1055
+ const height = Math.ceil(flowVertical ? flowTotal : crossTotal);
1056
+ const arcs = [];
1057
+ for (const a of ast.arcs) {
1058
+ const from = placed.get(a.from);
1059
+ const to = placed.get(a.to);
1060
+ if (!from || !to) continue;
1061
+ const start = boundaryPoint(from, to.x, to.y);
1062
+ const end = boundaryPoint(to, from.x, from.y);
1063
+ const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
1064
+ const arc = {
1065
+ from: a.from,
1066
+ to: a.to,
1067
+ kind: a.kind,
1068
+ path: `M ${round(start.x)} ${round(start.y)} L ${round(end.x)} ${round(end.y)}`,
1069
+ tip: { x: end.x, y: end.y, angle },
1070
+ labelAt: { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }
1071
+ };
1072
+ if (a.label !== void 0) arc.label = a.label;
1073
+ arcs.push(arc);
1074
+ }
1075
+ return {
1076
+ width,
1077
+ height,
1078
+ nodes: Array.from(placed.values()),
1079
+ arcs,
1080
+ title: ast.title,
1081
+ direction: ast.direction
1082
+ };
1083
+ }
1084
+ function boundaryPoint(n, tx, ty) {
1085
+ const dx = tx - n.x;
1086
+ const dy = ty - n.y;
1087
+ if (dx === 0 && dy === 0) return { x: n.x, y: n.y };
1088
+ const hw = n.width / 2;
1089
+ const hh = n.height / 2;
1090
+ if (n.node.kind === "chance") {
1091
+ const denom = Math.sqrt(dx * dx / (hw * hw) + dy * dy / (hh * hh));
1092
+ return { x: n.x + dx / denom, y: n.y + dy / denom };
1093
+ }
1094
+ const scaleX = dx !== 0 ? hw / Math.abs(dx) : Infinity;
1095
+ const scaleY = dy !== 0 ? hh / Math.abs(dy) : Infinity;
1096
+ const scale = Math.min(scaleX, scaleY);
1097
+ return { x: n.x + dx * scale, y: n.y + dy * scale };
1098
+ }
1099
+ function round(n) {
1100
+ return Math.round(n * 100) / 100;
1101
+ }
1102
+
1103
+ // src/diagrams/decisiontree/influence-renderer.ts
1104
+ function buildCss(t) {
1105
+ return `
1106
+ .lt-dtree { font-family: system-ui, -apple-system, sans-serif; }
1107
+ .lt-dtree-title { font: 500 16px sans-serif; fill: ${t.text}; }
1108
+ .lt-dtree-decision { fill: #dbeafe; stroke: #1d4ed8; stroke-width: 1.6; }
1109
+ .lt-dtree-chance { fill: #fef3c7; stroke: #b45309; stroke-width: 1.6; }
1110
+ .lt-dtree-value { fill: #dcfce7; stroke: #15803d; stroke-width: 1.6; }
1111
+ .lt-dtree-node-label { font: 500 12px sans-serif; fill: ${t.text}; text-anchor: middle; dominant-baseline: middle; }
1112
+ .lt-dtree-value-util { font: 600 10px "SF Mono", monospace; fill: ${t.textMuted}; text-anchor: middle; }
1113
+ .lt-dtree-arc { fill: none; stroke: ${t.stroke}; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
1114
+ .lt-dtree-arc-information { fill: none; stroke: ${t.stroke}; stroke-width: 1.6; stroke-dasharray: 5 4; stroke-linecap: round; }
1115
+ .lt-dtree-arc-head { fill: ${t.stroke}; stroke: none; }
1116
+ .lt-dtree-arc-label { font: 500 10px sans-serif; fill: ${t.textMuted}; text-anchor: middle; dominant-baseline: middle; }
1117
+ .lt-dtree-arc-label-bg { fill: ${t.bg}; stroke: none; }
1118
+ `.trim();
1119
+ }
1120
+ function renderNode(ln) {
1121
+ const n = ln.node;
1122
+ const parts = [];
1123
+ const hw = ln.width / 2;
1124
+ const hh = ln.height / 2;
1125
+ if (n.kind === "decision") {
1126
+ parts.push(rect({
1127
+ x: ln.x - hw,
1128
+ y: ln.y - hh,
1129
+ width: ln.width,
1130
+ height: ln.height,
1131
+ rx: 3,
1132
+ ry: 3,
1133
+ class: "lt-dtree-decision"
1134
+ }));
1135
+ } else if (n.kind === "chance") {
1136
+ parts.push(el("ellipse", {
1137
+ cx: ln.x,
1138
+ cy: ln.y,
1139
+ rx: hw,
1140
+ ry: hh,
1141
+ class: "lt-dtree-chance"
1142
+ }));
1143
+ } else {
1144
+ const ix = Math.min(14, ln.width * 0.2);
1145
+ const iy = Math.min(10, hh * 0.55);
1146
+ const pts = [
1147
+ `${ln.x - hw + ix},${ln.y - hh}`,
1148
+ `${ln.x + hw - ix},${ln.y - hh}`,
1149
+ `${ln.x + hw},${ln.y - hh + iy}`,
1150
+ `${ln.x + hw},${ln.y + hh - iy}`,
1151
+ `${ln.x + hw - ix},${ln.y + hh}`,
1152
+ `${ln.x - hw + ix},${ln.y + hh}`,
1153
+ `${ln.x - hw},${ln.y + hh - iy}`,
1154
+ `${ln.x - hw},${ln.y - hh + iy}`
1155
+ ].join(" ");
1156
+ parts.push(polygon({ points: pts, class: "lt-dtree-value" }));
1157
+ }
1158
+ const hasUtil = n.kind === "value" && n.utility !== void 0;
1159
+ const labelY = hasUtil ? ln.y - 6 : ln.y;
1160
+ parts.push(text({ x: ln.x, y: labelY, class: "lt-dtree-node-label" }, n.label));
1161
+ if (hasUtil) {
1162
+ parts.push(text({ x: ln.x, y: ln.y + 11, class: "lt-dtree-value-util" }, `U=${formatNum(n.utility)}`));
1163
+ }
1164
+ return group({
1165
+ "data-node-id": n.id,
1166
+ "data-node-kind": n.kind,
1167
+ "data-layer": String(ln.layer)
1168
+ }, parts);
1169
+ }
1170
+ function arrowHead(arc) {
1171
+ const size = 9;
1172
+ const rad = arc.tip.angle * Math.PI / 180;
1173
+ const tx = arc.tip.x;
1174
+ const ty = arc.tip.y;
1175
+ const spread = 25 * Math.PI / 180;
1176
+ const bx1 = tx - size * Math.cos(rad - spread);
1177
+ const by1 = ty - size * Math.sin(rad - spread);
1178
+ const bx2 = tx - size * Math.cos(rad + spread);
1179
+ const by2 = ty - size * Math.sin(rad + spread);
1180
+ const pts = `${round2(tx)},${round2(ty)} ${round2(bx1)},${round2(by1)} ${round2(bx2)},${round2(by2)}`;
1181
+ return polygon({ points: pts, class: "lt-dtree-arc-head" });
1182
+ }
1183
+ function renderArc(arc) {
1184
+ const parts = [];
1185
+ const cls = arc.kind === "information" ? "lt-dtree-arc-information" : "lt-dtree-arc";
1186
+ parts.push(path({ d: arc.path, class: cls, "data-arc": `${arc.from}->${arc.to}`, "data-arc-kind": arc.kind }));
1187
+ parts.push(arrowHead(arc));
1188
+ if (arc.label && arc.labelAt) {
1189
+ const w = Math.max(arc.label.length * 5.6 + 8, 16);
1190
+ parts.push(rect({
1191
+ x: arc.labelAt.x - w / 2,
1192
+ y: arc.labelAt.y - 7,
1193
+ width: w,
1194
+ height: 14,
1195
+ rx: 3,
1196
+ ry: 3,
1197
+ class: "lt-dtree-arc-label-bg"
1198
+ }));
1199
+ parts.push(text({ x: arc.labelAt.x, y: arc.labelAt.y, class: "lt-dtree-arc-label" }, arc.label));
1200
+ }
1201
+ return group({}, parts);
1202
+ }
1203
+ function formatNum(n) {
1204
+ if (Math.abs(n) >= 1e4) return n.toLocaleString(void 0, { maximumFractionDigits: 0 });
1205
+ if (Number.isInteger(n)) return String(n);
1206
+ return n.toFixed(2);
1207
+ }
1208
+ function round2(n) {
1209
+ return Math.round(n * 100) / 100;
1210
+ }
1211
+ function renderInfluence(ast, config) {
1212
+ const t = resolveBaseTheme(config?.theme ?? "default");
1213
+ const layout = layoutInfluence(ast);
1214
+ const titleOffset = ast.title ? 36 : 10;
1215
+ const width = Math.ceil(layout.width);
1216
+ const height = Math.ceil(layout.height + titleOffset);
1217
+ const children = [];
1218
+ children.push(title(ast.title ?? "Influence Diagram"));
1219
+ children.push(desc(
1220
+ `Influence diagram with ${layout.nodes.length} nodes and ${layout.arcs.length} arcs`
1221
+ ));
1222
+ children.push(el("style", {}, buildCss(t)));
1223
+ if (ast.title) {
1224
+ children.push(text({ x: 20, y: 24, class: "lt-dtree-title" }, ast.title));
1225
+ }
1226
+ const inner = [];
1227
+ for (const arc of layout.arcs) inner.push(renderArc(arc));
1228
+ for (const ln of layout.nodes) inner.push(renderNode(ln));
1229
+ children.push(group({ transform: `translate(0, ${titleOffset})`, "data-mode": "influence" }, inner));
1230
+ return svgRoot({
1231
+ class: "lt-dtree",
1232
+ role: "img",
1233
+ "aria-label": escapeXml(ast.title ?? "Influence diagram"),
1234
+ width,
1235
+ height,
1236
+ viewBox: `0 0 ${width} ${height}`
1237
+ }, children);
1238
+ }
1239
+
744
1240
  // src/diagrams/decisiontree/renderer.ts
745
1241
  var CLASS_PALETTE = [
746
1242
  "#0ea5e9",
@@ -754,7 +1250,7 @@ var CLASS_PALETTE = [
754
1250
  "#06b6d4",
755
1251
  "#f97316"
756
1252
  ];
757
- function buildCss(t) {
1253
+ function buildCss2(t) {
758
1254
  return `
759
1255
  .lt-dtree { font-family: system-ui, -apple-system, sans-serif; }
760
1256
  .lt-dtree-title { font: 500 16px sans-serif; fill: ${t.text}; }
@@ -819,7 +1315,7 @@ function renderDecisionNode(ln, layout) {
819
1315
  y: ln.y + ln.height / 2 + 13,
820
1316
  class: "lt-dtree-ev",
821
1317
  "text-anchor": "middle"
822
- }, `EV=${formatNum(n.ev)}`));
1318
+ }, `EV=${formatNum2(n.ev)}`));
823
1319
  }
824
1320
  } else if (n.kind === "chance") {
825
1321
  parts.push(circle({
@@ -851,7 +1347,7 @@ function renderDecisionNode(ln, layout) {
851
1347
  y: ln.y + ln.height / 2 + 13,
852
1348
  class: n.optimal ? "lt-dtree-ev-optimal" : "lt-dtree-ev",
853
1349
  "text-anchor": "middle"
854
- }, `EV=${formatNum(n.ev)}`));
1350
+ }, `EV=${formatNum2(n.ev)}`));
855
1351
  }
856
1352
  } else if (n.kind === "end") {
857
1353
  const halfW = ln.width / 2;
@@ -907,7 +1403,7 @@ function renderDecisionNode(ln, layout) {
907
1403
  "data-ev": n.ev !== void 0 ? String(n.ev) : ""
908
1404
  }, parts);
909
1405
  }
910
- function formatNum(n) {
1406
+ function formatNum2(n) {
911
1407
  if (Math.abs(n) >= 1e4) return n.toLocaleString(void 0, { maximumFractionDigits: 0 });
912
1408
  if (Number.isInteger(n)) return String(n);
913
1409
  return n.toFixed(2);
@@ -958,7 +1454,7 @@ function renderMlNode(ln, ast) {
958
1454
  let textY = y + 16;
959
1455
  const lineH = 14;
960
1456
  if (n.kind === "split" && n.feature) {
961
- const thresh = typeof n.threshold === "number" ? formatNum(n.threshold) : n.threshold ?? "";
1457
+ const thresh = typeof n.threshold === "number" ? formatNum2(n.threshold) : n.threshold ?? "";
962
1458
  parts.push(text(
963
1459
  { x: textX, y: textY, class: "lt-dtree-ml-line-1", "text-anchor": "middle" },
964
1460
  `${n.feature} ${n.op ?? ""} ${thresh}`
@@ -972,7 +1468,7 @@ function renderMlNode(ln, ast) {
972
1468
  const impName = ast.impurityName ?? "gini";
973
1469
  parts.push(text(
974
1470
  { x: textX, y: textY, class: "lt-dtree-ml-line-muted", "text-anchor": "middle" },
975
- `${impName} = ${formatNum(n.impurity)}`
1471
+ `${impName} = ${formatNum2(n.impurity)}`
976
1472
  ));
977
1473
  textY += lineH;
978
1474
  }
@@ -984,7 +1480,7 @@ function renderMlNode(ln, ast) {
984
1480
  textY += lineH;
985
1481
  }
986
1482
  if (n.value !== void 0) {
987
- const vStr = Array.isArray(n.value) ? `value = [${n.value.join(", ")}]` : `value = ${formatNum(n.value)}`;
1483
+ const vStr = Array.isArray(n.value) ? `value = [${n.value.join(", ")}]` : `value = ${formatNum2(n.value)}`;
988
1484
  parts.push(text({ x: textX, y: textY, class: "lt-dtree-ml-mono", "text-anchor": "middle" }, vStr));
989
1485
  textY += lineH;
990
1486
  }
@@ -1002,7 +1498,7 @@ function renderMlNode(ln, ast) {
1002
1498
  } else if (typeof n.value === "number") {
1003
1499
  parts.push(text(
1004
1500
  { x: textX, y: textY, class: "lt-dtree-ml-class", "text-anchor": "middle", fill: "#0f172a" },
1005
- `predicted = ${formatNum(n.value)}`
1501
+ `predicted = ${formatNum2(n.value)}`
1006
1502
  ));
1007
1503
  textY += lineH;
1008
1504
  }
@@ -1078,6 +1574,7 @@ function wrapText(text2, maxChars) {
1078
1574
  return lines.slice(0, 2);
1079
1575
  }
1080
1576
  function renderDecisionTree(ast, config) {
1577
+ if ("arcs" in ast) return renderInfluence(ast, config);
1081
1578
  const t = resolveBaseTheme(config?.theme ?? "default");
1082
1579
  const layout = layoutDecisionTree(ast);
1083
1580
  const titleOffset = ast.title ? 36 : 10;
@@ -1086,7 +1583,7 @@ function renderDecisionTree(ast, config) {
1086
1583
  const children = [];
1087
1584
  children.push(title(ast.title ?? "Decision Tree"));
1088
1585
  children.push(desc(`Decision tree (${ast.mode} mode) with ${layout.nodes.length} nodes and ${layout.edges.length} edges`));
1089
- children.push(el("style", {}, buildCss(t)));
1586
+ children.push(el("style", {}, buildCss2(t)));
1090
1587
  if (ast.title) {
1091
1588
  children.push(text({ x: 20, y: 24, class: "lt-dtree-title" }, ast.title));
1092
1589
  }
@@ -1249,7 +1746,7 @@ var TimelineParseError = class extends Error {
1249
1746
  column;
1250
1747
  source;
1251
1748
  };
1252
- function preprocess2(src) {
1749
+ function preprocess3(src) {
1253
1750
  const out = [];
1254
1751
  const lines = src.split(/\r?\n/);
1255
1752
  for (let i = 0; i < lines.length; i++) {
@@ -1363,7 +1860,7 @@ function splitDateAndBody(s, lineNum) {
1363
1860
  return { date: datePart, body };
1364
1861
  }
1365
1862
  function parseTimeline(src) {
1366
- const lines = preprocess2(src);
1863
+ const lines = preprocess3(src);
1367
1864
  if (!lines.length) throw new TimelineParseError("Empty timeline");
1368
1865
  const ast = {
1369
1866
  type: "timeline",
@@ -2702,7 +3199,7 @@ var MERMAID_STEREOTYPE = {
2702
3199
  join: "join",
2703
3200
  end: "final"
2704
3201
  };
2705
- function preprocess3(src) {
3202
+ function preprocess4(src) {
2706
3203
  const out = [];
2707
3204
  const lines = src.split(/\r?\n/);
2708
3205
  for (let i = 0; i < lines.length; i++) {
@@ -2716,7 +3213,7 @@ function preprocess3(src) {
2716
3213
  }
2717
3214
  return out;
2718
3215
  }
2719
- function unquote2(s) {
3216
+ function unquote3(s) {
2720
3217
  if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
2721
3218
  return s.slice(1, -1);
2722
3219
  }
@@ -2729,7 +3226,7 @@ function parseProps(s) {
2729
3226
  for (const part of inner.split(",")) {
2730
3227
  const idx = part.indexOf(":");
2731
3228
  if (idx < 0) continue;
2732
- out[part.slice(0, idx).trim()] = unquote2(part.slice(idx + 1).trim());
3229
+ out[part.slice(0, idx).trim()] = unquote3(part.slice(idx + 1).trim());
2733
3230
  }
2734
3231
  return out;
2735
3232
  }
@@ -2870,7 +3367,7 @@ function isIdent(tok) {
2870
3367
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(tok);
2871
3368
  }
2872
3369
  function parseStateDiagram(src) {
2873
- const lines = preprocess3(src);
3370
+ const lines = preprocess4(src);
2874
3371
  if (lines.length === 0) {
2875
3372
  throw new StateParseError("Empty document");
2876
3373
  }
@@ -2892,7 +3389,7 @@ function parseStateDiagram(src) {
2892
3389
  if (props.direction === "TB" || props.direction === "LR") direction = props.direction;
2893
3390
  beforeProps = headerRest.slice(0, propsMatch.index).trim();
2894
3391
  }
2895
- if (beforeProps.startsWith('"')) title2 = unquote2(beforeProps);
3392
+ if (beforeProps.startsWith('"')) title2 = unquote3(beforeProps);
2896
3393
  else if (beforeProps.length > 0) title2 = beforeProps;
2897
3394
  const ctx = {
2898
3395
  states: [],
@@ -2979,7 +3476,7 @@ function parseStateDiagram(src) {
2979
3476
  const stateLabelMatch = text2.match(/^state\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/);
2980
3477
  if (stateLabelMatch) {
2981
3478
  const node = ensureSimpleState(ctx, stateLabelMatch[1], parent);
2982
- node.label = unquote2(stateLabelMatch[2].trim());
3479
+ node.label = unquote3(stateLabelMatch[2].trim());
2983
3480
  i++;
2984
3481
  continue;
2985
3482
  }
@@ -3114,7 +3611,7 @@ function parseStateDiagram(src) {
3114
3611
  const labelOnlyMatch = text2.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/);
3115
3612
  if (labelOnlyMatch && isIdent(labelOnlyMatch[1])) {
3116
3613
  const node = ensureSimpleState(ctx, labelOnlyMatch[1], parent);
3117
- node.label = unquote2(labelOnlyMatch[2].trim());
3614
+ node.label = unquote3(labelOnlyMatch[2].trim());
3118
3615
  i++;
3119
3616
  continue;
3120
3617
  }
@@ -3842,7 +4339,7 @@ function renderPseudo(node) {
3842
4339
  return "";
3843
4340
  }
3844
4341
  }
3845
- function renderNode(node) {
4342
+ function renderNode2(node) {
3846
4343
  if (node.node.kind === "pseudo") return renderPseudo(node);
3847
4344
  return renderSimple(node);
3848
4345
  }
@@ -3924,7 +4421,7 @@ function renderLayout(layout) {
3924
4421
  titleNode,
3925
4422
  // Composite clusters first so simple-state bodies sit on top.
3926
4423
  group({ class: "lt-clusters" }, layout.clusters.map(renderComposite)),
3927
- group({ class: "lt-state-bodies" }, layout.nodes.map(renderNode)),
4424
+ group({ class: "lt-state-bodies" }, layout.nodes.map(renderNode2)),
3928
4425
  group({ class: "lt-edges" }, layout.edges.map(renderEdge)),
3929
4426
  group({ class: "lt-notes" }, layout.notes.map(renderNote))
3930
4427
  ]
@@ -4005,7 +4502,7 @@ var INST_CATEGORIES = /* @__PURE__ */ new Set([
4005
4502
  "local_discrete",
4006
4503
  "local_shared"
4007
4504
  ]);
4008
- function preprocess4(src) {
4505
+ function preprocess5(src) {
4009
4506
  const out = [];
4010
4507
  const lines = src.split(/\r?\n/);
4011
4508
  for (let i = 0; i < lines.length; i++) {
@@ -4025,7 +4522,7 @@ function preprocess4(src) {
4025
4522
  }
4026
4523
  return out;
4027
4524
  }
4028
- function unquote3(s) {
4525
+ function unquote4(s) {
4029
4526
  const t = s.trim();
4030
4527
  if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) {
4031
4528
  return t.slice(1, -1).replace(/\\"/g, '"');
@@ -4054,7 +4551,7 @@ function parseAttrList(inside) {
4054
4551
  const idx = p.indexOf(":");
4055
4552
  if (idx < 0) continue;
4056
4553
  const key = p.slice(0, idx).trim();
4057
- const val = unquote3(p.slice(idx + 1).trim());
4554
+ const val = unquote4(p.slice(idx + 1).trim());
4058
4555
  out[key] = val;
4059
4556
  }
4060
4557
  return out;
@@ -4089,7 +4586,7 @@ function parseAnchor(tok) {
4089
4586
  return { id: tok.slice(0, dot), port: tok.slice(dot + 1) };
4090
4587
  }
4091
4588
  function parsePid(src) {
4092
- const lines = preprocess4(src);
4589
+ const lines = preprocess5(src);
4093
4590
  if (lines.length === 0) {
4094
4591
  throw new PidParseError("Empty document");
4095
4592
  }
@@ -4103,7 +4600,7 @@ function parsePid(src) {
4103
4600
  const { rest, attrs: headerAttrs } = extractAttrs(headerRest);
4104
4601
  if (headerAttrs.direction === "TB") direction = "TB";
4105
4602
  if (headerAttrs.direction === "LR") direction = "LR";
4106
- if (rest) title2 = unquote3(rest);
4603
+ if (rest) title2 = unquote4(rest);
4107
4604
  const equipment = [];
4108
4605
  const linesAst = [];
4109
4606
  const instruments = [];
@@ -5076,7 +5573,7 @@ function renderInstrument(category, letterCode, loopNumber) {
5076
5573
  }
5077
5574
 
5078
5575
  // src/diagrams/pid/layout.ts
5079
- var PADDING = 30;
5576
+ var PADDING2 = 30;
5080
5577
  var TITLE_AREA = 26;
5081
5578
  var EQUIP_GAP_X = 70;
5082
5579
  var INST_RADIUS = 14;
@@ -5143,8 +5640,8 @@ function layoutPid(ast) {
5143
5640
  const equipById = /* @__PURE__ */ new Map();
5144
5641
  const heights = ast.equipment.map((e) => GEOMETRY[e.equipType]?.height ?? 60);
5145
5642
  const maxH = Math.max(...heights, 0);
5146
- const rowY = PADDING + TITLE_AREA + maxH / 2 + 30;
5147
- let cursorX = PADDING + 40;
5643
+ const rowY = PADDING2 + TITLE_AREA + maxH / 2 + 30;
5644
+ let cursorX = PADDING2 + 40;
5148
5645
  for (const equip of ast.equipment) {
5149
5646
  const geo = GEOMETRY[equip.equipType] ?? { width: 60, height: 40, ports: {} };
5150
5647
  const cx = cursorX + geo.width / 2;
@@ -5187,24 +5684,24 @@ function layoutPid(ast) {
5187
5684
  };
5188
5685
  const instruments = [];
5189
5686
  const instById = /* @__PURE__ */ new Map();
5190
- const crBandY = PADDING + TITLE_AREA + 40;
5687
+ const crBandY = PADDING2 + TITLE_AREA + 40;
5191
5688
  let crSlot = 0;
5192
5689
  for (const inst of ast.instruments) {
5193
5690
  let cx = 0;
5194
5691
  let cy = 0;
5195
5692
  if (inst.category.startsWith("cr_")) {
5196
5693
  const tgt = inst.controls ?? inst.measures ?? "";
5197
- cx = targetX(tgt, PADDING + 80 + crSlot * (INST_RADIUS * 2 + 28));
5694
+ cx = targetX(tgt, PADDING2 + 80 + crSlot * (INST_RADIUS * 2 + 28));
5198
5695
  cy = crBandY;
5199
5696
  crSlot += 1;
5200
5697
  } else if (inst.category.startsWith("local_")) {
5201
5698
  cy = rowY + maxH / 2 + INST_OFFSET + INST_RADIUS;
5202
5699
  const tgt = inst.measures ?? inst.controls ?? "";
5203
- cx = targetX(tgt, PADDING + 80);
5700
+ cx = targetX(tgt, PADDING2 + 80);
5204
5701
  } else {
5205
5702
  cy = rowY + maxH / 2 + INST_OFFSET;
5206
5703
  const tgt = inst.measures ?? inst.controls ?? "";
5207
- cx = targetX(tgt, PADDING + 80);
5704
+ cx = targetX(tgt, PADDING2 + 80);
5208
5705
  }
5209
5706
  const lay = {
5210
5707
  inst,
@@ -5248,8 +5745,8 @@ function layoutPid(ast) {
5248
5745
  const maxX = Math.max(...allX);
5249
5746
  const maxY = Math.max(...allY);
5250
5747
  return {
5251
- width: Math.max(maxX + PADDING, 400),
5252
- height: Math.max(maxY + PADDING, 200),
5748
+ width: Math.max(maxX + PADDING2, 400),
5749
+ height: Math.max(maxY + PADDING2, 200),
5253
5750
  equipment,
5254
5751
  instruments,
5255
5752
  lines,
@@ -5680,7 +6177,7 @@ var PrismaParseError = class extends Error {
5680
6177
  }
5681
6178
  line;
5682
6179
  };
5683
- function preprocess5(src) {
6180
+ function preprocess6(src) {
5684
6181
  const out = [];
5685
6182
  const rows = src.split(/\r?\n/);
5686
6183
  for (let i = 0; i < rows.length; i++) {
@@ -5953,7 +6450,7 @@ var STAGE_KEYS = /* @__PURE__ */ new Set([
5953
6450
  "included"
5954
6451
  ]);
5955
6452
  function parsePrisma(src) {
5956
- const lines = preprocess5(src);
6453
+ const lines = preprocess6(src);
5957
6454
  if (lines.length === 0) throw new PrismaParseError("empty input");
5958
6455
  const header = lines.shift();
5959
6456
  if (header.indent !== 0 || header.text.toLowerCase() !== "prisma") {
@@ -5981,10 +6478,10 @@ function parsePrisma(src) {
5981
6478
  validateCounts = parseValidate(value, l.line);
5982
6479
  break;
5983
6480
  case "title":
5984
- title2 = unquote4(value);
6481
+ title2 = unquote5(value);
5985
6482
  break;
5986
6483
  case "review-id":
5987
- reviewId = unquote4(value);
6484
+ reviewId = unquote5(value);
5988
6485
  break;
5989
6486
  case "direction":
5990
6487
  if (!/^(TB|TD)$/i.test(value.trim())) {
@@ -6072,7 +6569,7 @@ function parsePrisma(src) {
6072
6569
  runArithmeticValidation(ast);
6073
6570
  return ast;
6074
6571
  }
6075
- function unquote4(s) {
6572
+ function unquote5(s) {
6076
6573
  const t = s.trim();
6077
6574
  if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
6078
6575
  return t;
@@ -6886,7 +7383,7 @@ function headerHeightFor(lineCount) {
6886
7383
  }
6887
7384
 
6888
7385
  // src/diagrams/prisma/renderer.ts
6889
- function buildCss2(t) {
7386
+ function buildCss3(t) {
6890
7387
  return `
6891
7388
  .prisma { font-family: system-ui, -apple-system, sans-serif; }
6892
7389
  .prisma-title { font: 600 17px sans-serif; fill: ${t.text}; }
@@ -7116,7 +7613,7 @@ function renderPrismaLayout(layout, config) {
7116
7613
  `PRISMA 2020 flow diagram (${layout.mode}, ${layout.kind}) \u2014 ${layout.boxes.length} boxes, ${layout.edges.length} arrows` + (layout.warnings.length > 0 ? `. Warnings: ${layout.warnings.join("; ")}` : "")
7117
7614
  )
7118
7615
  );
7119
- children.push(el("style", {}, buildCss2(t)));
7616
+ children.push(el("style", {}, buildCss3(t)));
7120
7617
  children.push(arrowMarker(t));
7121
7618
  if (layout.title) {
7122
7619
  children.push(
@@ -7208,7 +7705,7 @@ var UsecaseParseError = class extends Error {
7208
7705
  if (line2 !== void 0) this.line = line2;
7209
7706
  }
7210
7707
  };
7211
- function preprocess6(src) {
7708
+ function preprocess7(src) {
7212
7709
  const out = [];
7213
7710
  const rows = src.split(/\r?\n/);
7214
7711
  for (let i = 0; i < rows.length; i++) {
@@ -7691,7 +8188,7 @@ function parseUsecase(src) {
7691
8188
  const state2 = {
7692
8189
  ast,
7693
8190
  idTable: /* @__PURE__ */ new Map(),
7694
- lines: preprocess6(src),
8191
+ lines: preprocess7(src),
7695
8192
  i: 0
7696
8193
  };
7697
8194
  parseHeader(state2);
@@ -8139,10 +8636,10 @@ function layoutUsecase(ast) {
8139
8636
  const maxY2 = Math.max(...ys);
8140
8637
  if (useTree) {
8141
8638
  const legPaths2 = [
8142
- `M ${round(busX)} ${round(minY2)} L ${round(busX)} ${round(maxY2)}`
8639
+ `M ${round3(busX)} ${round3(minY2)} L ${round3(busX)} ${round3(maxY2)}`
8143
8640
  ];
8144
8641
  for (const c of childAnchors) {
8145
- legPaths2.push(`M ${round(c.p.x)} ${round(c.p.y)} L ${round(busX)} ${round(c.p.y)}`);
8642
+ legPaths2.push(`M ${round3(c.p.x)} ${round3(c.p.y)} L ${round3(busX)} ${round3(c.p.y)}`);
8146
8643
  handledGen.add(c.r);
8147
8644
  }
8148
8645
  trees.push({
@@ -8151,14 +8648,14 @@ function layoutUsecase(ast) {
8151
8648
  stemX: busX,
8152
8649
  stemTop: minY2,
8153
8650
  stemBottom: maxY2,
8154
- trunkD: `M ${round(busX)} ${round(pAnchor.y)} L ${round(pAnchor.x)} ${round(pAnchor.y)}`,
8651
+ trunkD: `M ${round3(busX)} ${round3(pAnchor.y)} L ${round3(pAnchor.x)} ${round3(pAnchor.y)}`,
8155
8652
  legPaths: legPaths2
8156
8653
  });
8157
8654
  } else {
8158
8655
  for (const c of childAnchors) {
8159
8656
  edges.push({
8160
8657
  relation: c.r,
8161
- d: `M ${round(c.p.x)} ${round(c.p.y)} L ${round(busX)} ${round(c.p.y)} L ${round(busX)} ${round(pAnchor.y)} L ${round(pAnchor.x)} ${round(pAnchor.y)}`,
8658
+ d: `M ${round3(c.p.x)} ${round3(c.p.y)} L ${round3(busX)} ${round3(c.p.y)} L ${round3(busX)} ${round3(pAnchor.y)} L ${round3(pAnchor.x)} ${round3(pAnchor.y)}`,
8162
8659
  arrowKind: "hollow",
8163
8660
  dashed: false
8164
8661
  });
@@ -8183,7 +8680,7 @@ function layoutUsecase(ast) {
8183
8680
  const legPaths = [];
8184
8681
  for (const r5 of rels) {
8185
8682
  const cPt = perimeter(r5.source, jx, jy);
8186
- legPaths.push(`M ${round(cPt.x)} ${round(cPt.y)} L ${round(jx)} ${round(jy)}`);
8683
+ legPaths.push(`M ${round3(cPt.x)} ${round3(cPt.y)} L ${round3(jx)} ${round3(jy)}`);
8187
8684
  handledGen.add(r5);
8188
8685
  }
8189
8686
  trees.push({
@@ -8192,7 +8689,7 @@ function layoutUsecase(ast) {
8192
8689
  stemX: jx,
8193
8690
  stemTop: jy,
8194
8691
  stemBottom: jy,
8195
- trunkD: `M ${round(jx)} ${round(jy)} L ${round(pPt.x)} ${round(pPt.y)}`,
8692
+ trunkD: `M ${round3(jx)} ${round3(jy)} L ${round3(pPt.x)} ${round3(pPt.y)}`,
8196
8693
  legPaths
8197
8694
  });
8198
8695
  }
@@ -8208,7 +8705,7 @@ function layoutUsecase(ast) {
8208
8705
  const busX = side === "left" ? Math.min(pAnchor.x, cAnchor.x) - 20 : Math.max(pAnchor.x, cAnchor.x) + 20;
8209
8706
  edges.push({
8210
8707
  relation: r5,
8211
- d: `M ${round(cAnchor.x)} ${round(cAnchor.y)} L ${round(busX)} ${round(cAnchor.y)} L ${round(busX)} ${round(pAnchor.y)} L ${round(pAnchor.x)} ${round(pAnchor.y)}`,
8708
+ d: `M ${round3(cAnchor.x)} ${round3(cAnchor.y)} L ${round3(busX)} ${round3(cAnchor.y)} L ${round3(busX)} ${round3(pAnchor.y)} L ${round3(pAnchor.x)} ${round3(pAnchor.y)}`,
8212
8709
  arrowKind: "hollow",
8213
8710
  dashed: false
8214
8711
  });
@@ -8234,7 +8731,7 @@ function layoutUsecase(ast) {
8234
8731
  else if (r5.kind === "generalization") arrowKind2 = "hollow";
8235
8732
  const edge = {
8236
8733
  relation: r5,
8237
- d: `M ${round(pa.x)} ${round(pa.y)} L ${round(pb.x)} ${round(pb.y)}`,
8734
+ d: `M ${round3(pa.x)} ${round3(pa.y)} L ${round3(pb.x)} ${round3(pb.y)}`,
8238
8735
  arrowKind: arrowKind2,
8239
8736
  dashed
8240
8737
  };
@@ -8303,14 +8800,14 @@ function placeMultiplicity(near, far, text2) {
8303
8800
  const oy = dy / len * 14;
8304
8801
  const px = -dy / len * 9;
8305
8802
  const py = dx / len * 9;
8306
- return { text: text2, x: round(near.x + ox + px), y: round(near.y + oy + py) };
8803
+ return { text: text2, x: round3(near.x + ox + px), y: round3(near.y + oy + py) };
8307
8804
  }
8308
- function round(n) {
8805
+ function round3(n) {
8309
8806
  return Math.round(n * 100) / 100;
8310
8807
  }
8311
8808
 
8312
8809
  // src/diagrams/usecase/renderer.ts
8313
- function buildCss3(t) {
8810
+ function buildCss4(t) {
8314
8811
  const ucFill = "#eaf2fc";
8315
8812
  const ucStroke = "#5b85c0";
8316
8813
  const subjectStroke = "#c2cede";
@@ -8589,7 +9086,7 @@ function renderUsecaseLayout(layout, config) {
8589
9086
  `${layout.subject ? "Subject: " + layout.subject.name + ". " : ""}${layout.actors.length} actors, ${layout.usecases.length} use cases, ${nInclude} include, ${nExtend} extend.`
8590
9087
  )
8591
9088
  );
8592
- children.push(el("style", {}, buildCss3(t)));
9089
+ children.push(el("style", {}, buildCss4(t)));
8593
9090
  children.push(markers(t));
8594
9091
  if (layout.title) {
8595
9092
  children.push(
@@ -8686,7 +9183,7 @@ var UNIT_SUFFIX = {
8686
9183
  function normalizeQuotes(s) {
8687
9184
  return s.replace(/[“”〝〞"]/g, '"').replace(/[‘’]/g, "'").replace(/[「」『』]/g, '"');
8688
9185
  }
8689
- function preprocess7(src) {
9186
+ function preprocess8(src) {
8690
9187
  const out = [];
8691
9188
  const rows = normalizeQuotes(src).split(/\r?\n/);
8692
9189
  for (let i = 0; i < rows.length; i++) {
@@ -8936,7 +9433,7 @@ function parsePert(src) {
8936
9433
  tasks: [],
8937
9434
  warnings: []
8938
9435
  };
8939
- const lines = preprocess7(src);
9436
+ const lines = preprocess8(src);
8940
9437
  if (lines.length === 0) {
8941
9438
  throw new PertParseError("empty document \u2014 expected 'pert' header", 1);
8942
9439
  }
@@ -9136,13 +9633,13 @@ function schedulePert(ast) {
9136
9633
  const realOrder = order.filter((n) => n !== START && n !== FINISH);
9137
9634
  let negativeSlack = false;
9138
9635
  for (const id of realOrder) {
9139
- const slack = round2(ls.get(id) - es.get(id));
9636
+ const slack = round4(ls.get(id) - es.get(id));
9140
9637
  if (slack < -1e-9) negativeSlack = true;
9141
9638
  computed.set(id, {
9142
- es: round2(es.get(id)),
9143
- ef: round2(ef.get(id)),
9144
- ls: round2(ls.get(id)),
9145
- lf: round2(lf.get(id)),
9639
+ es: round4(es.get(id)),
9640
+ ef: round4(ef.get(id)),
9641
+ ls: round4(ls.get(id)),
9642
+ lf: round4(lf.get(id)),
9146
9643
  slack,
9147
9644
  critical: slack <= tol + 1e-9
9148
9645
  });
@@ -9157,8 +9654,8 @@ function schedulePert(ast) {
9157
9654
  for (const t of ast.tasks) {
9158
9655
  if (t.variance !== void 0 && computed.get(t.id).critical) sum += t.variance;
9159
9656
  }
9160
- projectVariance = round2(sum);
9161
- projectStdDev = round2(Math.sqrt(sum));
9657
+ projectVariance = round4(sum);
9658
+ projectStdDev = round4(Math.sqrt(sum));
9162
9659
  }
9163
9660
  const depCount = ast.tasks.reduce((n, t) => n + t.deps.length, 0);
9164
9661
  if (negativeSlack) {
@@ -9169,7 +9666,7 @@ function schedulePert(ast) {
9169
9666
  const result = {
9170
9667
  computed,
9171
9668
  order: realOrder,
9172
- projectDuration: round2(projectDuration),
9669
+ projectDuration: round4(projectDuration),
9173
9670
  criticalPath,
9174
9671
  criticalCount,
9175
9672
  depCount
@@ -9211,7 +9708,7 @@ function extractCriticalPath(ast, g, computed) {
9211
9708
  }
9212
9709
  return path2;
9213
9710
  }
9214
- function round2(n) {
9711
+ function round4(n) {
9215
9712
  return Math.round(n * 1e4) / 1e4;
9216
9713
  }
9217
9714
 
@@ -9362,8 +9859,8 @@ function layoutAoa(ast, schedule) {
9362
9859
  x: cx[e],
9363
9860
  y: cy[e],
9364
9861
  r: AOA.R,
9365
- te: round3(te[e]),
9366
- tl: round3(tl[e]),
9862
+ te: round5(te[e]),
9863
+ tl: round5(tl[e]),
9367
9864
  critical: eventCritical(e)
9368
9865
  }));
9369
9866
  const outArcs = arcs.map((a) => {
@@ -9422,7 +9919,7 @@ function arcGeometry(tx, ty, hx, hy, r5) {
9422
9919
  const sy = ty + uy * r5;
9423
9920
  const ex = hx - ux * r5;
9424
9921
  const ey = hy - uy * r5;
9425
- return { d: `M ${round3(sx)} ${round3(sy)} L ${round3(ex)} ${round3(ey)}`, mx: (sx + ex) / 2, my: (sy + ey) / 2 };
9922
+ return { d: `M ${round5(sx)} ${round5(sy)} L ${round5(ex)} ${round5(ey)}`, mx: (sx + ex) / 2, my: (sy + ey) / 2 };
9426
9923
  }
9427
9924
  function buildAoaSummary(ast, sched) {
9428
9925
  const summary = {
@@ -9436,7 +9933,7 @@ function buildAoaSummary(ast, sched) {
9436
9933
  if (sched.projectStdDev !== void 0) summary.projectStdDev = sched.projectStdDev;
9437
9934
  return summary;
9438
9935
  }
9439
- function round3(n) {
9936
+ function round5(n) {
9440
9937
  return Math.round(n * 1e3) / 1e3;
9441
9938
  }
9442
9939
 
@@ -10053,7 +10550,7 @@ var PALETTE = {
10053
10550
  axisBaseline: "#8497ad",
10054
10551
  grid: "#e3eaf3"
10055
10552
  };
10056
- function buildCss4(t) {
10553
+ function buildCss5(t) {
10057
10554
  const P = PALETTE;
10058
10555
  return `
10059
10556
  .sx-pert { font-family: system-ui, -apple-system, sans-serif; }
@@ -10377,7 +10874,7 @@ function renderPertLayout(layout, config) {
10377
10874
  `${layout.summary.taskCount} activities, project duration ${fmtVal(layout.summary.projectDuration)} ${unitWord(layout.unit)}` + (cp.length ? `, critical path ${cp.join(" \u2192 ")}` : "") + "."
10378
10875
  )
10379
10876
  );
10380
- children.push(el("style", {}, buildCss4(t)));
10877
+ children.push(el("style", {}, buildCss5(t)));
10381
10878
  children.push(markers2());
10382
10879
  if (layout.title) {
10383
10880
  children.push(
@@ -11292,7 +11789,7 @@ function layoutSequence(ast) {
11292
11789
  // src/diagrams/sequence/renderer.ts
11293
11790
  var HEAD = "#e8f0fb";
11294
11791
  var HEAD_STROKE = "#5b85c0";
11295
- function buildCss5(t) {
11792
+ function buildCss6(t) {
11296
11793
  return `
11297
11794
  .sx-seq { font-family: system-ui, -apple-system, sans-serif; }
11298
11795
  .sx-seq-axis { stroke: ${t.neutral}; stroke-width: 1; stroke-dasharray: 4 4; }
@@ -11587,7 +12084,7 @@ function renderSequenceLayout(layout, config) {
11587
12084
  `${layout.lifelines.length} participants, ${nMsg} messages, ${nFrag} combined fragments.`
11588
12085
  )
11589
12086
  );
11590
- children.push(el("style", {}, buildCss5(t)));
12087
+ children.push(el("style", {}, buildCss6(t)));
11591
12088
  children.push(markers3(t));
11592
12089
  const titleBand = layout.title ? 32 : 0;
11593
12090
  if (layout.title) {
@@ -11658,7 +12155,7 @@ var PetriParseError = class extends Error {
11658
12155
  };
11659
12156
  var OPENERS = ['"', "\u201C", "\u300C", "\u300E", "\xAB"];
11660
12157
  var CLOSERS = ['"', "\u201D", "\u300D", "\u300F", "\xBB"];
11661
- function tokenize3(s) {
12158
+ function tokenize4(s) {
11662
12159
  const out = [];
11663
12160
  let i = 0;
11664
12161
  while (i < s.length) {
@@ -11725,7 +12222,7 @@ function parsePetri(text2) {
11725
12222
  if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
11726
12223
  if (!sawHeader && /^petri(net)?\b/i.test(trimmed)) {
11727
12224
  sawHeader = true;
11728
- const toks = tokenize3(trimmed);
12225
+ const toks = tokenize4(trimmed);
11729
12226
  const titleTok = toks.find((t, idx) => idx > 0 && t.quoted);
11730
12227
  if (titleTok) ast.title = titleTok.text;
11731
12228
  else if (toks[1] && !toks[1].quoted) ast.title = toks.slice(1).map((t) => t.text).join(" ");
@@ -11787,7 +12284,7 @@ function declareDup(id, lineNo, placeIds, transIds) {
11787
12284
  }
11788
12285
  }
11789
12286
  function parsePlace(line2, lineNo, placeIds, transIds) {
11790
- const toks = tokenize3(normalizeKeyNums(line2));
12287
+ const toks = tokenize4(normalizeKeyNums(line2));
11791
12288
  const idTok = toks[1];
11792
12289
  if (!idTok || idTok.quoted) throw new PetriParseError(`place is missing an id`, lineNo);
11793
12290
  const id = idTok.text;
@@ -11826,7 +12323,7 @@ function parseTransition(line2, lineNo, placeIds, transIds) {
11826
12323
  guard = String(g).trim();
11827
12324
  return " ";
11828
12325
  });
11829
- const toks = tokenize3(normalizeKeyNums(stripped));
12326
+ const toks = tokenize4(normalizeKeyNums(stripped));
11830
12327
  const idTok = toks[1];
11831
12328
  if (!idTok || idTok.quoted) throw new PetriParseError(`transition is missing an id`, lineNo);
11832
12329
  const id = idTok.text;
@@ -11884,7 +12381,7 @@ function parseArc(m, lineNo, placeIds, transIds) {
11884
12381
  throw new PetriParseError(`${type} arcs are place\u2192transition only`, lineNo);
11885
12382
  }
11886
12383
  const arc = { from, to, type, weight: 1, line: lineNo };
11887
- const toks = tokenize3(normalizeKeyNums(rest));
12384
+ const toks = tokenize4(normalizeKeyNums(rest));
11888
12385
  const labelParts = [];
11889
12386
  for (const t of toks) {
11890
12387
  if (t.quoted) {
@@ -12021,7 +12518,7 @@ function layoutPetri(ast) {
12021
12518
  reindex();
12022
12519
  }
12023
12520
  }
12024
- const sizeOf2 = (id) => {
12521
+ const sizeOf3 = (id) => {
12025
12522
  if (kindOf.get(id) === "place") return { halfW: C2.PLACE_R, halfH: C2.PLACE_R, r: C2.PLACE_R };
12026
12523
  const tr = ast.transitions.find((t) => t.id === id);
12027
12524
  const long = tr.kind === "timed" ? C2.TRANS_BOX_H : C2.TRANS_BAR_H;
@@ -12031,11 +12528,11 @@ function layoutPetri(ast) {
12031
12528
  return { halfW, halfH, r: 0 };
12032
12529
  };
12033
12530
  const flowHalf = (id) => {
12034
- const s = sizeOf2(id);
12531
+ const s = sizeOf3(id);
12035
12532
  return dir === "lr" ? s.halfW : s.halfH;
12036
12533
  };
12037
12534
  const crossHalf = (id) => {
12038
- const s = sizeOf2(id);
12535
+ const s = sizeOf3(id);
12039
12536
  return dir === "lr" ? s.halfH : s.halfW;
12040
12537
  };
12041
12538
  const layerHalf = layers.map((arr) => Math.max(0, ...arr.map(flowHalf)));
@@ -12058,7 +12555,7 @@ function layoutPetri(ast) {
12058
12555
  const flow = flowCenter[L];
12059
12556
  const cx = dir === "lr" ? flow : cross;
12060
12557
  const cy = dir === "lr" ? cross : flow;
12061
- const s = sizeOf2(id);
12558
+ const s = sizeOf3(id);
12062
12559
  geom.set(id, { id, kind: kindOf.get(id), cx, cy, halfW: s.halfW, halfH: s.halfH, r: s.r, layer: L });
12063
12560
  });
12064
12561
  });
@@ -12252,7 +12749,7 @@ function detectSubclass(ast) {
12252
12749
  }
12253
12750
 
12254
12751
  // src/diagrams/petri/renderer.ts
12255
- function buildCss6(t) {
12752
+ function buildCss7(t) {
12256
12753
  return `
12257
12754
  .sx-petri { font-family: system-ui, -apple-system, sans-serif; }
12258
12755
  .sx-petri-place { fill: ${t.placeFill}; stroke: ${t.placeStroke}; stroke-width: 2; }
@@ -12396,7 +12893,7 @@ function arcPath(ag) {
12396
12893
  }
12397
12894
  return `M ${p[0].x} ${p[0].y} L ${p[p.length - 1].x} ${p[p.length - 1].y}`;
12398
12895
  }
12399
- function renderArc(ag) {
12896
+ function renderArc2(ag) {
12400
12897
  const parts = [];
12401
12898
  let cls = "sx-petri-arc";
12402
12899
  let markerEnd;
@@ -12443,14 +12940,14 @@ function renderPetriLayout(layout, config) {
12443
12940
  ].filter(Boolean);
12444
12941
  children.push(title(`Petri net${layout.title ? " \u2014 " + layout.title : ""}`));
12445
12942
  children.push(desc(descParts.join(" ")));
12446
- children.push(el("style", {}, buildCss6(t)));
12943
+ children.push(el("style", {}, buildCss7(t)));
12447
12944
  children.push(markers4(t));
12448
12945
  const titleBand = layout.title ? 32 : 0;
12449
12946
  if (layout.title) {
12450
12947
  children.push(text({ x: layout.width / 2, y: 22, class: "sx-petri-title", "text-anchor": "middle" }, layout.title));
12451
12948
  }
12452
12949
  const body = [];
12453
- body.push(group({ class: "sx-petri-arcs" }, layout.arcs.map(renderArc)));
12950
+ body.push(group({ class: "sx-petri-arcs" }, layout.arcs.map(renderArc2)));
12454
12951
  body.push(group({ class: "sx-petri-places" }, layout.places.map((p) => renderPlace(p, layout.ast.tokenStyle))));
12455
12952
  body.push(group({ class: "sx-petri-transitions" }, layout.transitions.map(renderTransition)));
12456
12953
  children.push(titleBand ? group({ transform: `translate(0, ${titleBand})` }, body) : group({}, body));
@@ -12623,7 +13120,7 @@ function splitStatements(line2) {
12623
13120
  out.push(buf);
12624
13121
  return out;
12625
13122
  }
12626
- function tokenize4(raw) {
13123
+ function tokenize5(raw) {
12627
13124
  const line2 = stripComment2(raw);
12628
13125
  const strings = [];
12629
13126
  const masked = line2.replace(QUOTE_RE, (...m) => {
@@ -12778,7 +13275,7 @@ function parseNetwork(text2) {
12778
13275
  });
12779
13276
  for (const stmt of statements) {
12780
13277
  const lineNo = stmt.no;
12781
- const toks = tokenize4(stmt.text);
13278
+ const toks = tokenize5(stmt.text);
12782
13279
  if (toks.length === 0) continue;
12783
13280
  const t0 = toks[0];
12784
13281
  if (!headerSeen) {
@@ -13764,7 +14261,7 @@ function layoutNetwork2(ast) {
13764
14261
 
13765
14262
  // src/diagrams/network/renderer.ts
13766
14263
  var r22 = (n) => Math.round(n * 100) / 100;
13767
- function buildCss7(t) {
14264
+ function buildCss8(t) {
13768
14265
  return `
13769
14266
  .sx-net { font-family: system-ui, -apple-system, sans-serif; }
13770
14267
  .sx-net-body { fill: ${t.deviceFill}; stroke: ${t.deviceStroke}; stroke-width: 2; }
@@ -13824,7 +14321,7 @@ function annotation(link) {
13824
14321
  if (link.label) parts.push(link.label);
13825
14322
  return parts.join(" \xB7 ");
13826
14323
  }
13827
- function arrowHead(x1, y1, x2, y2, color, hs = 6) {
14324
+ function arrowHead2(x1, y1, x2, y2, color, hs = 6) {
13828
14325
  const ang = Math.atan2(y2 - y1, x2 - x1);
13829
14326
  const a1 = ang + Math.PI - 0.45;
13830
14327
  const a2 = ang + Math.PI + 0.45;
@@ -13849,7 +14346,7 @@ function renderLink(lg, t) {
13849
14346
  } else {
13850
14347
  parts.push(el("polyline", { class: cls, stroke: color, points: lg.points.map((p) => `${r22(p.x)},${r22(p.y)}`).join(" ") }));
13851
14348
  }
13852
- if (link.directed) parts.push(arrowHead(p1.x, p1.y, p2.x, p2.y, color));
14349
+ if (link.directed) parts.push(arrowHead2(p1.x, p1.y, p2.x, p2.y, color));
13853
14350
  if (link.linkType === "fiber") {
13854
14351
  const ang = Math.atan2(p2.y - p1.y, p2.x - p1.x) + Math.PI / 2;
13855
14352
  for (const f of [0.4, 0.55]) {
@@ -13923,7 +14420,7 @@ function renderNetworkLayout(layout, config) {
13923
14420
  ].filter(Boolean);
13924
14421
  children.push(title(`Network diagram${layout.title ? " \u2014 " + layout.title : ""}`));
13925
14422
  children.push(desc(descParts.join(" ")));
13926
- children.push(el("style", {}, buildCss7(t)));
14423
+ children.push(el("style", {}, buildCss8(t)));
13927
14424
  const titleBand = layout.title ? 30 : 0;
13928
14425
  if (layout.title) {
13929
14426
  children.push(text({ x: r22(layout.width / 2), y: 21, class: "sx-net-title", "text-anchor": "middle" }, layout.title));
@@ -15562,10 +16059,10 @@ function buildTree2(kind, parent, children, vertical) {
15562
16059
  const avgChildX = childXs.reduce((a, b) => a + b, 0) / childXs.length;
15563
16060
  const trunkX = Math.max(parent.x + 10, Math.min(parent.x + parent.width - 10, avgChildX));
15564
16061
  const trunkBaseY = parentBottom + UMLCLASS_CONST.TRIANGLE_H;
15565
- const trunkD2 = `M ${round4(trunkX)} ${round4(junctionY)} L ${round4(trunkX)} ${round4(trunkBaseY)}`;
16062
+ const trunkD2 = `M ${round6(trunkX)} ${round6(junctionY)} L ${round6(trunkX)} ${round6(trunkBaseY)}`;
15566
16063
  const legPaths2 = children.map((c) => {
15567
16064
  const cx = c.box.x + c.box.width / 2;
15568
- return `M ${round4(cx)} ${round4(c.box.y)} L ${round4(cx)} ${round4(junctionY)} L ${round4(trunkX)} ${round4(junctionY)}`;
16065
+ return `M ${round6(cx)} ${round6(c.box.y)} L ${round6(cx)} ${round6(junctionY)} L ${round6(trunkX)} ${round6(junctionY)}`;
15569
16066
  });
15570
16067
  return {
15571
16068
  parentId: parent.classifier.id,
@@ -15585,10 +16082,10 @@ function buildTree2(kind, parent, children, vertical) {
15585
16082
  const avgChildY = childYs.reduce((a, b) => a + b, 0) / childYs.length;
15586
16083
  const trunkY = Math.max(parent.y + 10, Math.min(parent.y + parent.height - 10, avgChildY));
15587
16084
  const trunkBaseX = parentRight + UMLCLASS_CONST.TRIANGLE_H;
15588
- const trunkD = `M ${round4(junctionX)} ${round4(trunkY)} L ${round4(trunkBaseX)} ${round4(trunkY)}`;
16085
+ const trunkD = `M ${round6(junctionX)} ${round6(trunkY)} L ${round6(trunkBaseX)} ${round6(trunkY)}`;
15589
16086
  const legPaths = children.map((c) => {
15590
16087
  const cy = c.box.y + c.box.height / 2;
15591
- return `M ${round4(c.box.x)} ${round4(cy)} L ${round4(junctionX)} ${round4(cy)} L ${round4(junctionX)} ${round4(trunkY)}`;
16088
+ return `M ${round6(c.box.x)} ${round6(cy)} L ${round6(junctionX)} ${round6(cy)} L ${round6(junctionX)} ${round6(trunkY)}`;
15592
16089
  });
15593
16090
  return {
15594
16091
  parentId: parent.classifier.id,
@@ -15651,7 +16148,7 @@ function adornmentBase(end, outset) {
15651
16148
  }
15652
16149
  }
15653
16150
  function polyline(pts) {
15654
- return pts.map((p, i) => `${i === 0 ? "M" : "L"} ${round4(p.x)} ${round4(p.y)}`).join(" ");
16151
+ return pts.map((p, i) => `${i === 0 ? "M" : "L"} ${round6(p.x)} ${round6(p.y)}`).join(" ");
15655
16152
  }
15656
16153
  function midOf(pts) {
15657
16154
  if (pts.length === 0) return { x: 0, y: 0 };
@@ -15659,7 +16156,7 @@ function midOf(pts) {
15659
16156
  const a = pts[i], b = pts[i + 1] ?? pts[i];
15660
16157
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
15661
16158
  }
15662
- function round4(n) {
16159
+ function round6(n) {
15663
16160
  return Math.round(n * 10) / 10;
15664
16161
  }
15665
16162
 
@@ -18248,6 +18745,24 @@ function splitWordsWithTrailingSpace(s) {
18248
18745
  return out.length > 0 ? out : [s];
18249
18746
  }
18250
18747
 
18748
+ // src/diagrams/mindmap/modes.ts
18749
+ var EXTENDED_MODES = [
18750
+ "futureswheel",
18751
+ "driver"
18752
+ ];
18753
+ function baseStyleFor(mode) {
18754
+ if (mode === "futureswheel") return "map";
18755
+ if (mode === "driver") return "logic-right";
18756
+ return mode;
18757
+ }
18758
+ var modeRegistry = /* @__PURE__ */ new WeakMap();
18759
+ function setMode(ast, mode) {
18760
+ modeRegistry.set(ast, mode);
18761
+ }
18762
+ function modeOf(ast) {
18763
+ return modeRegistry.get(ast) ?? ast.style;
18764
+ }
18765
+
18251
18766
  // src/diagrams/mindmap/parser.ts
18252
18767
  var MindmapParseError = class extends Error {
18253
18768
  constructor(message, line2, column, source) {
@@ -18271,6 +18786,10 @@ function parseDirective(line2, out) {
18271
18786
  const val = body.slice(idx + 1).trim();
18272
18787
  if (key === "style" && VALID_STYLES.includes(val)) {
18273
18788
  out.style = val;
18789
+ out.mode = val;
18790
+ } else if (key === "style" && EXTENDED_MODES.includes(val)) {
18791
+ out.mode = val;
18792
+ out.style = baseStyleFor(out.mode);
18274
18793
  } else if (key === "theme") {
18275
18794
  out.themeOverride = val;
18276
18795
  } else if (key === "maxlabelwidth") {
@@ -18289,7 +18808,7 @@ function parseMindmap(text2) {
18289
18808
  lineOffset = 1;
18290
18809
  }
18291
18810
  const lines = allLines;
18292
- const directives = { style: "map", maxLabelWidth: DEFAULT_MAX_LABEL_WIDTH };
18811
+ const directives = { style: "map", mode: "map", maxLabelWidth: DEFAULT_MAX_LABEL_WIDTH };
18293
18812
  let root = null;
18294
18813
  let rootInferred;
18295
18814
  let idCounter2 = 0;
@@ -18364,6 +18883,7 @@ function parseMindmap(text2) {
18364
18883
  };
18365
18884
  if (rootInferred) ast.rootInferred = rootInferred;
18366
18885
  if (directives.themeOverride) ast.themeOverride = directives.themeOverride;
18886
+ setMode(ast, directives.mode);
18367
18887
  return ast;
18368
18888
  }
18369
18889
 
@@ -18396,8 +18916,127 @@ function lintMindmap(text2) {
18396
18916
  return out;
18397
18917
  }
18398
18918
 
18919
+ // src/diagrams/mindmap/futureswheel.ts
18920
+ var RING_GAP = 150;
18921
+ function spokePath(x1, y1, x2, y2) {
18922
+ return `M ${x1.toFixed(1)} ${y1.toFixed(1)} L ${x2.toFixed(1)} ${y2.toFixed(1)}`;
18923
+ }
18924
+ function edgeWidthForOrder(order) {
18925
+ if (order <= 1) return 2.2;
18926
+ if (order === 2) return 1.6;
18927
+ return 1.2;
18928
+ }
18929
+ function leafCount(node) {
18930
+ if (node.children.length === 0) return 1;
18931
+ let sum = 0;
18932
+ for (const c of node.children) sum += leafCount(c);
18933
+ return sum;
18934
+ }
18935
+ function layoutFuturesWheel(ast) {
18936
+ const root = ast.root;
18937
+ const mw = ast.maxLabelWidth;
18938
+ const nodes = [];
18939
+ const placed = /* @__PURE__ */ new Map();
18940
+ const rootM = measureLabel(root, widthBudget(0, mw));
18941
+ const rootLayout = {
18942
+ node: root,
18943
+ x: 0,
18944
+ y: 0,
18945
+ side: "center",
18946
+ branchIndex: -1,
18947
+ labelWidth: rootM.width,
18948
+ labelHeight: rootM.height,
18949
+ fontSize: fontSizeOf(0),
18950
+ lines: rootM.lines
18951
+ };
18952
+ nodes.push(rootLayout);
18953
+ placed.set(root.id, { layout: rootLayout, angle: 0 });
18954
+ const placeChildren = (node, a0, a1, branchIndex) => {
18955
+ const kids = node.children;
18956
+ if (kids.length === 0) return;
18957
+ const weights = kids.map(leafCount);
18958
+ const totalW = weights.reduce((s, w) => s + w, 0);
18959
+ const span = a1 - a0;
18960
+ let cursor = a0;
18961
+ for (let i = 0; i < kids.length; i++) {
18962
+ const child = kids[i];
18963
+ const slice = weights[i] / totalW * span;
18964
+ const sliceStart = cursor;
18965
+ const sliceEnd = cursor + slice;
18966
+ const mid = (sliceStart + sliceEnd) / 2;
18967
+ cursor = sliceEnd;
18968
+ const order = child.depth;
18969
+ const radius = order * RING_GAP;
18970
+ const m = measureLabel(child, widthBudget(child.depth, mw));
18971
+ const childBranch = node.depth === 0 ? i : branchIndex;
18972
+ const layout = {
18973
+ node: child,
18974
+ x: radius * Math.cos(mid),
18975
+ y: radius * Math.sin(mid),
18976
+ // Right half-plane → label extends right; left half-plane → left.
18977
+ side: Math.cos(mid) >= 0 ? "right" : "left",
18978
+ branchIndex: childBranch,
18979
+ labelWidth: m.width,
18980
+ labelHeight: m.height,
18981
+ fontSize: fontSizeOf(child.depth),
18982
+ lines: m.lines
18983
+ };
18984
+ nodes.push(layout);
18985
+ placed.set(child.id, { layout, angle: mid });
18986
+ placeChildren(child, sliceStart, sliceEnd, childBranch);
18987
+ }
18988
+ };
18989
+ const START2 = -Math.PI / 2;
18990
+ placeChildren(root, START2, START2 + Math.PI * 2, -1);
18991
+ const edges = [];
18992
+ const walkEdges = (parent) => {
18993
+ const p = placed.get(parent.id);
18994
+ if (!p) return;
18995
+ for (const c of parent.children) {
18996
+ const cp = placed.get(c.id);
18997
+ if (!cp) continue;
18998
+ edges.push({
18999
+ from: parent.id,
19000
+ to: c.id,
19001
+ path: spokePath(p.layout.x, p.layout.y, cp.layout.x, cp.layout.y),
19002
+ color: "",
19003
+ width: edgeWidthForOrder(c.depth)
19004
+ });
19005
+ walkEdges(c);
19006
+ }
19007
+ };
19008
+ walkEdges(root);
19009
+ const { width, height } = normalize(nodes);
19010
+ const byId = new Map(nodes.map((n) => [n.node.id, n]));
19011
+ for (const e of edges) {
19012
+ const f = byId.get(e.from);
19013
+ const t = byId.get(e.to);
19014
+ if (f && t) e.path = spokePath(f.x, f.y, t.x, t.y);
19015
+ }
19016
+ return {
19017
+ width,
19018
+ height,
19019
+ // `style` is constrained to the base union; futureswheel renders on `map`.
19020
+ style: "map",
19021
+ nodes,
19022
+ edges,
19023
+ title: ast.title
19024
+ };
19025
+ }
19026
+ function wheelCenter(result) {
19027
+ for (const n of result.nodes) {
19028
+ if (n.node.depth === 0) return { cx: n.x, cy: n.y };
19029
+ }
19030
+ return { cx: result.width / 2, cy: result.height / 2 };
19031
+ }
19032
+ function maxOrder(result) {
19033
+ let max = 0;
19034
+ for (const n of result.nodes) if (n.node.depth > max) max = n.node.depth;
19035
+ return max;
19036
+ }
19037
+
18399
19038
  // src/diagrams/mindmap/layout.ts
18400
- var PADDING2 = 40;
19039
+ var PADDING3 = 40;
18401
19040
  var SIBLING_GAP = 18;
18402
19041
  var MAIN_GAP = 44;
18403
19042
  var UNDERLINE_GAP = 4;
@@ -18544,13 +19183,13 @@ function normalize(nodes) {
18544
19183
  minY = Math.min(minY, n.y - lh / 2);
18545
19184
  maxY = Math.max(maxY, n.y + lh / 2);
18546
19185
  }
18547
- const dx = PADDING2 - minX;
18548
- const dy = PADDING2 - minY;
19186
+ const dx = PADDING3 - minX;
19187
+ const dy = PADDING3 - minY;
18549
19188
  for (const n of nodes) {
18550
19189
  n.x += dx;
18551
19190
  n.y += dy;
18552
19191
  }
18553
- return { width: maxX - minX + PADDING2 * 2, height: maxY - minY + PADDING2 * 2 };
19192
+ return { width: maxX - minX + PADDING3 * 2, height: maxY - minY + PADDING3 * 2 };
18554
19193
  }
18555
19194
  function underlineY(n) {
18556
19195
  return n.y + n.labelHeight / 2 - UNDERLINE_GAP / 2;
@@ -18665,6 +19304,9 @@ function layoutLogicRight(ast) {
18665
19304
  return { width, height, style: "logic-right", nodes, edges, title: ast.title };
18666
19305
  }
18667
19306
  function layoutMindmap(ast) {
19307
+ const mode = modeOf(ast);
19308
+ if (mode === "futureswheel") return layoutFuturesWheel(ast);
19309
+ if (mode === "driver") return layoutLogicRight(ast);
18668
19310
  const style = ast.style;
18669
19311
  if (style === "logic-right") return layoutLogicRight(ast);
18670
19312
  return layoutMap(ast);
@@ -18793,7 +19435,7 @@ function tspan(attrs, content) {
18793
19435
  }
18794
19436
  return `<tspan ${pairs.join(" ")}>${escapeXml(content)}</tspan>`;
18795
19437
  }
18796
- function renderNode2(n, color, theme, fontFamily) {
19438
+ function renderNode3(n, color, theme, fontFamily, orderClass) {
18797
19439
  const isRoot = n.node.depth === 0;
18798
19440
  const isMain = n.node.depth === 1;
18799
19441
  const fs = n.fontSize;
@@ -18826,12 +19468,14 @@ function renderNode2(n, color, theme, fontFamily) {
18826
19468
  "stroke-linecap": "round"
18827
19469
  })
18828
19470
  );
18829
- const cls = isRoot ? "schematex-mindmap-central" : isMain ? "schematex-mindmap-main" : "schematex-mindmap-leaf";
19471
+ const baseCls = isRoot ? "schematex-mindmap-central" : isMain ? "schematex-mindmap-main" : "schematex-mindmap-leaf";
19472
+ const cls = orderClass ? `${baseCls} mm-order-${n.node.depth}` : baseCls;
18830
19473
  return group(
18831
19474
  {
18832
19475
  class: cls,
18833
19476
  "data-node-id": n.node.id,
18834
19477
  "data-depth": n.node.depth,
19478
+ "data-order": orderClass ? n.node.depth : void 0,
18835
19479
  "data-branch-idx": n.branchIndex
18836
19480
  },
18837
19481
  [...decorations, ...children]
@@ -18839,8 +19483,30 @@ function renderNode2(n, color, theme, fontFamily) {
18839
19483
  }
18840
19484
  function renderMindmapAST(ast, themeName = "default", fontFamily = "system-ui, -apple-system, sans-serif") {
18841
19485
  const theme = resolveMindmapTheme(ast.themeOverride ?? themeName);
19486
+ const mode = modeOf(ast);
19487
+ const isWheel = mode === "futureswheel";
18842
19488
  const layout = layoutMindmap(ast);
18843
19489
  const byId = new Map(layout.nodes.map((n) => [n.node.id, n]));
19490
+ const ringSvgs = [];
19491
+ if (isWheel) {
19492
+ const { cx, cy } = wheelCenter(layout);
19493
+ const rings = maxOrder(layout);
19494
+ for (let order = 1; order <= rings; order++) {
19495
+ ringSvgs.push(
19496
+ circle({
19497
+ cx: cx.toFixed(1),
19498
+ cy: cy.toFixed(1),
19499
+ r: (order * RING_GAP).toFixed(1),
19500
+ fill: "none",
19501
+ stroke: theme.centralFill,
19502
+ "stroke-width": 1,
19503
+ "stroke-dasharray": "3 5",
19504
+ opacity: 0.25,
19505
+ class: `mm-ring mm-ring-${order}`
19506
+ })
19507
+ );
19508
+ }
19509
+ }
18844
19510
  const edgeSvgs = [];
18845
19511
  for (const e of layout.edges) {
18846
19512
  const target = byId.get(e.to);
@@ -18859,9 +19525,10 @@ function renderMindmapAST(ast, themeName = "default", fontFamily = "system-ui, -
18859
19525
  const nodeSvgs = [];
18860
19526
  for (const n of layout.nodes) {
18861
19527
  const color = n.node.depth === 0 ? theme.centralFill : paletteColor(theme, n.branchIndex);
18862
- nodeSvgs.push(renderNode2(n, color, theme, fontFamily));
19528
+ nodeSvgs.push(renderNode3(n, color, theme, fontFamily, isWheel));
18863
19529
  }
18864
19530
  const title2 = ast.title ?? tokensToPlainText(ast.root.tokens);
19531
+ const descLabel = isWheel ? `futures-wheel mindmap with ${layout.nodes.length} nodes` : `${layout.style} mindmap with ${layout.nodes.length} nodes`;
18865
19532
  return svgRoot(
18866
19533
  {
18867
19534
  viewBox: `0 0 ${layout.width.toFixed(1)} ${layout.height.toFixed(1)}`,
@@ -18872,8 +19539,9 @@ function renderMindmapAST(ast, themeName = "default", fontFamily = "system-ui, -
18872
19539
  },
18873
19540
  [
18874
19541
  title(title2),
18875
- desc(`${layout.style} mindmap with ${layout.nodes.length} nodes`),
19542
+ desc(descLabel),
18876
19543
  rect({ x: 0, y: 0, width: layout.width, height: layout.height, fill: theme.bg }),
19544
+ group({ class: "schematex-mindmap-rings", "aria-hidden": "true" }, ringSvgs),
18877
19545
  group({ class: "schematex-mindmap-edges", "aria-hidden": "true" }, edgeSvgs),
18878
19546
  group({ class: "schematex-mindmap-nodes" }, nodeSvgs)
18879
19547
  ]
@@ -19064,6 +19732,30 @@ var DEFAULT_CONFIG = {
19064
19732
  function emptyAxis() {
19065
19733
  return { low: "", high: "" };
19066
19734
  }
19735
+ function emptySipoc() {
19736
+ return { suppliers: [], inputs: [], process: [], outputs: [], customers: [] };
19737
+ }
19738
+ function emptyQfd() {
19739
+ return { whats: [], hows: [], relationships: [], roof: [], normalize: false };
19740
+ }
19741
+ function parseItemList(raw) {
19742
+ return parseNumberList2(raw);
19743
+ }
19744
+ function parseStrength(tok) {
19745
+ const t = tok.trim().toLowerCase();
19746
+ if (t === "9" || t === "strong" || t === "\u25CF" || t === "\u25C9" || t === "\u2B24") return 9;
19747
+ if (t === "3" || t === "medium" || t === "\u25CB" || t === "\u25EF" || t === "o") return 3;
19748
+ if (t === "1" || t === "weak" || t === "\u25B3" || t === "\u25BD" || t === "\u25B2") return 1;
19749
+ return void 0;
19750
+ }
19751
+ function parseCorrelation(tok) {
19752
+ const t = tok.trim();
19753
+ if (t === "++" || /^strong\+?$/i.test(t) || t === "\u25CE") return "++";
19754
+ if (t === "+" || /^pos(itive)?$/i.test(t) || t === "\u25CB") return "+";
19755
+ if (t === "--" || t === "\u2212\u2212" || /^strong-$/i.test(t) || t === "\u2715" || t === "\u2716" || t === "\xD7") return "--";
19756
+ if (t === "-" || t === "\u2212" || /^neg(ative)?$/i.test(t) || t === "\u2717") return "-";
19757
+ return void 0;
19758
+ }
19067
19759
  function quadrantToCell(q) {
19068
19760
  switch (q) {
19069
19761
  case 1:
@@ -19216,6 +19908,91 @@ function parseCellLine(line2, st) {
19216
19908
  }
19217
19909
  return true;
19218
19910
  }
19911
+ var SIPOC_KEYS = [
19912
+ "suppliers",
19913
+ "inputs",
19914
+ "process",
19915
+ "outputs",
19916
+ "customers"
19917
+ ];
19918
+ function parseSipocLine(line2, ast) {
19919
+ if (!ast.sipoc) return false;
19920
+ const m = line2.match(/^([a-zA-Z]+)\s*:\s*(.*)$/);
19921
+ if (!m) return false;
19922
+ let key = m[1].toLowerCase();
19923
+ if (key === "supplier") key = "suppliers";
19924
+ else if (key === "input") key = "inputs";
19925
+ else if (key === "output") key = "outputs";
19926
+ else if (key === "customer") key = "customers";
19927
+ const col = SIPOC_KEYS.find((k) => k === key);
19928
+ if (!col) return false;
19929
+ const items = parseItemList(m[2]);
19930
+ ast.sipoc[col].push(...items);
19931
+ return true;
19932
+ }
19933
+ function parseQfdLine(line2, ast) {
19934
+ const qfd = ast.qfd;
19935
+ if (!qfd) return false;
19936
+ const normMatch = line2.match(/^normali[sz]e\s*:\s*(.+)$/i);
19937
+ if (normMatch) {
19938
+ const v = normMatch[1].trim().toLowerCase();
19939
+ qfd.normalize = v === "true" || v === "on" || v === "1" || v === "percent" || v === "%";
19940
+ return true;
19941
+ }
19942
+ const whatMatch = line2.match(/^what\s*:\s*(.*)$/i);
19943
+ if (whatMatch) {
19944
+ const q = readQuoted2(whatMatch[1], 0);
19945
+ if (!q) return true;
19946
+ const rest = whatMatch[1].slice(q.next);
19947
+ const wMatch = rest.match(/(?:weight|wt|imp(?:ortance)?)\s*:\s*(-?\d+(?:\.\d+)?)/i);
19948
+ const weight = wMatch ? Number(wMatch[1]) : 1;
19949
+ qfd.whats.push({ label: q.text, weight });
19950
+ return true;
19951
+ }
19952
+ const howMatch = line2.match(/^how\s*:\s*(.*)$/i);
19953
+ if (howMatch) {
19954
+ const q = readQuoted2(howMatch[1], 0);
19955
+ if (!q) return true;
19956
+ const rest = howMatch[1].slice(q.next);
19957
+ const dMatch = rest.match(/(?:dir(?:ection)?|target)\s*:\s*([a-zA-Z↑↓○]+)/i);
19958
+ let direction;
19959
+ if (dMatch) {
19960
+ const d = dMatch[1].toLowerCase();
19961
+ if (d === "up" || d === "max" || d === "maximise" || d === "maximize" || d === "\u2191") direction = "up";
19962
+ else if (d === "down" || d === "min" || d === "minimise" || d === "minimize" || d === "\u2193") direction = "down";
19963
+ else if (d === "target" || d === "nominal" || d === "\u25CB") direction = "target";
19964
+ }
19965
+ qfd.hows.push(direction ? { label: q.text, direction } : { label: q.text });
19966
+ return true;
19967
+ }
19968
+ const relMatch = line2.match(/^rel\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*:?\s*(.+)$/i);
19969
+ if (relMatch) {
19970
+ const what = Number(relMatch[1]);
19971
+ const how = Number(relMatch[2]);
19972
+ const strength = parseStrength(relMatch[3]);
19973
+ if (strength !== void 0) qfd.relationships.push({ what, how, strength });
19974
+ return true;
19975
+ }
19976
+ const cellMatch = line2.match(/^cell\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*(.*)$/i);
19977
+ if (cellMatch) {
19978
+ const how = Number(cellMatch[1]);
19979
+ const what = Number(cellMatch[2]);
19980
+ const valMatch = cellMatch[3].match(/value\s*:\s*(\d+)/i);
19981
+ const strength = valMatch ? parseStrength(valMatch[1]) : void 0;
19982
+ if (strength !== void 0) qfd.relationships.push({ what, how, strength });
19983
+ return true;
19984
+ }
19985
+ const roofMatch = line2.match(/^roof\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*:?\s*(.+)$/i);
19986
+ if (roofMatch) {
19987
+ let a = Number(roofMatch[1]);
19988
+ let b = Number(roofMatch[2]);
19989
+ if (a > b) [a, b] = [b, a];
19990
+ const correlation = parseCorrelation(roofMatch[3]);
19991
+ if (correlation !== void 0 && a !== b) qfd.roof.push({ a, b, correlation });
19992
+ return true;
19993
+ }
19994
+ return false;
19995
+ }
19219
19996
  function parseConfigLine(key, value, ast) {
19220
19997
  const k = key.trim().toLowerCase();
19221
19998
  const v = value.trim();
@@ -19258,6 +20035,26 @@ function parseHeader2(line2, ast) {
19258
20035
  if (title2) ast.title = stripQuotes4(title2);
19259
20036
  return void 0;
19260
20037
  }
20038
+ const sipocMatch = rest.match(/^sipoc\b\s*(.*)$/i);
20039
+ if (sipocMatch) {
20040
+ ast.mode = "sipoc";
20041
+ ast.grid = "NxM";
20042
+ ast.cols = 5;
20043
+ ast.rows = 0;
20044
+ ast.sipoc = emptySipoc();
20045
+ const title2 = sipocMatch[1].trim();
20046
+ if (title2) ast.title = stripQuotes4(title2);
20047
+ return void 0;
20048
+ }
20049
+ const qfdMatch = rest.match(/^qfd\b\s*(.*)$/i);
20050
+ if (qfdMatch) {
20051
+ ast.mode = "qfd";
20052
+ ast.grid = "NxM";
20053
+ ast.qfd = emptyQfd();
20054
+ const title2 = qfdMatch[1].trim();
20055
+ if (title2) ast.title = stripQuotes4(title2);
20056
+ return void 0;
20057
+ }
19261
20058
  const tokenMatch = rest.match(/^([a-zA-Z0-9_-]+)\s*(.*)$/);
19262
20059
  if (tokenMatch) {
19263
20060
  const tok = tokenMatch[1].toLowerCase();
@@ -19308,6 +20105,8 @@ function parseMatrix(text2) {
19308
20105
  st.ast.title = stripQuotes4(line2.replace(/^title\s*:\s*/i, ""));
19309
20106
  continue;
19310
20107
  }
20108
+ if (st.ast.mode === "sipoc" && parseSipocLine(line2, st.ast)) continue;
20109
+ if (st.ast.mode === "qfd" && parseQfdLine(line2, st.ast)) continue;
19311
20110
  if (/^x-axis\s*:/i.test(line2)) {
19312
20111
  st.ast.xAxis = parseAxis(line2.replace(/^x-axis\s*:\s*/i, ""));
19313
20112
  continue;
@@ -19406,10 +20205,27 @@ function parseMatrix(text2) {
19406
20205
  st.ast.template = templateName;
19407
20206
  }
19408
20207
  }
19409
- if (st.ast.cols === 3 && st.ast.rows === 3 && st.ast.grid !== "NxM") {
20208
+ if (st.ast.cols === 3 && st.ast.rows === 3 && st.ast.grid !== "NxM" && st.ast.mode !== "sipoc" && st.ast.mode !== "qfd") {
19410
20209
  st.ast.grid = "3x3";
19411
20210
  }
19412
- if (st.ast.mode === "heatmap" || st.ast.mode === "correlation") st.ast.grid = "NxM";
20211
+ if (st.ast.mode === "heatmap" || st.ast.mode === "correlation" || st.ast.mode === "sipoc" || st.ast.mode === "qfd") {
20212
+ st.ast.grid = "NxM";
20213
+ }
20214
+ if (st.ast.mode === "sipoc" && st.ast.sipoc) {
20215
+ const s = st.ast.sipoc;
20216
+ st.ast.cols = 5;
20217
+ st.ast.rows = Math.max(
20218
+ s.suppliers.length,
20219
+ s.inputs.length,
20220
+ s.process.length,
20221
+ s.outputs.length,
20222
+ s.customers.length
20223
+ );
20224
+ }
20225
+ if (st.ast.mode === "qfd" && st.ast.qfd) {
20226
+ st.ast.cols = st.ast.qfd.hows.length;
20227
+ st.ast.rows = st.ast.qfd.whats.length;
20228
+ }
19413
20229
  return st.ast;
19414
20230
  }
19415
20231
  function findCommentStart(line2) {
@@ -19427,6 +20243,23 @@ function findCommentStart(line2) {
19427
20243
  return -1;
19428
20244
  }
19429
20245
 
20246
+ // src/diagrams/matrix/types.ts
20247
+ function computeQfdImportance(qfd) {
20248
+ const raw = qfd.hows.map(() => 0);
20249
+ for (const r5 of qfd.relationships) {
20250
+ if (r5.how < 0 || r5.how >= qfd.hows.length) continue;
20251
+ if (r5.what < 0 || r5.what >= qfd.whats.length) continue;
20252
+ const w = qfd.whats[r5.what].weight;
20253
+ raw[r5.how] += w * r5.strength;
20254
+ }
20255
+ const total = raw.reduce((acc, v) => acc + v, 0);
20256
+ return raw.map((importance, how) => ({
20257
+ how,
20258
+ importance,
20259
+ percent: total > 0 ? Math.round(importance / total * 100) : 0
20260
+ }));
20261
+ }
20262
+
19430
20263
  // src/diagrams/matrix/layout.ts
19431
20264
  var CANVAS_W = 720;
19432
20265
  var CANVAS_H = 560;
@@ -19536,6 +20369,65 @@ function resolveLabelCollisions(points, plot, mode) {
19536
20369
  }
19537
20370
  }
19538
20371
  }
20372
+ var SIPOC_COL_W = 168;
20373
+ var SIPOC_X0 = 24;
20374
+ var SIPOC_Y0 = 24;
20375
+ var SIPOC_HEADER_H = 40;
20376
+ var SIPOC_ROW_H = 44;
20377
+ function layoutSipoc(ast) {
20378
+ const rows = Math.max(1, ast.rows);
20379
+ const titleH = ast.title ? 40 : 0;
20380
+ const y0 = SIPOC_Y0 + titleH;
20381
+ const canvasWidth = SIPOC_X0 * 2 + SIPOC_COL_W * 5;
20382
+ const canvasHeight = y0 + SIPOC_HEADER_H + rows * SIPOC_ROW_H + SIPOC_Y0;
20383
+ return {
20384
+ canvasWidth,
20385
+ canvasHeight,
20386
+ x0: SIPOC_X0,
20387
+ y0,
20388
+ colW: SIPOC_COL_W,
20389
+ headerH: SIPOC_HEADER_H,
20390
+ rowH: SIPOC_ROW_H,
20391
+ rows
20392
+ };
20393
+ }
20394
+ var QFD_CELL = 46;
20395
+ var QFD_WHAT_LABEL_W = 190;
20396
+ var QFD_WEIGHT_W = 46;
20397
+ var QFD_HOW_LABEL_H = 130;
20398
+ var QFD_FOOTER_H = 64;
20399
+ var QFD_PAD = 24;
20400
+ function layoutQfd(ast) {
20401
+ const cols = Math.max(1, ast.cols);
20402
+ const rows = Math.max(1, ast.rows);
20403
+ const cellW = QFD_CELL;
20404
+ const cellH = QFD_CELL;
20405
+ const titleH = ast.title ? 40 : 0;
20406
+ const roofH = Math.ceil(cellW / 2 * cols) + 8;
20407
+ const whatLabelW = QFD_WHAT_LABEL_W;
20408
+ const weightW = QFD_WEIGHT_W;
20409
+ const howLabelH = QFD_HOW_LABEL_H;
20410
+ const footerH = QFD_FOOTER_H;
20411
+ const gridX0 = QFD_PAD + whatLabelW + weightW;
20412
+ const gridY0 = QFD_PAD + titleH + roofH + howLabelH;
20413
+ const canvasWidth = gridX0 + cols * cellW + QFD_PAD;
20414
+ const canvasHeight = gridY0 + rows * cellH + footerH + QFD_PAD;
20415
+ return {
20416
+ canvasWidth,
20417
+ canvasHeight,
20418
+ gridX0,
20419
+ gridY0,
20420
+ cellW,
20421
+ cellH,
20422
+ cols,
20423
+ rows,
20424
+ whatLabelW,
20425
+ weightW,
20426
+ howLabelH,
20427
+ roofH,
20428
+ footerH
20429
+ };
20430
+ }
19539
20431
  function layoutMatrix(ast) {
19540
20432
  const canvasWidth = CANVAS_W;
19541
20433
  const canvasHeight = CANVAS_H;
@@ -19635,6 +20527,35 @@ var CSS = `
19635
20527
  .sx-matrix-leader { stroke: #94a3b8; stroke-width: 0.6; opacity: 0.7; fill: none; }
19636
20528
  .sx-matrix-legend-text { font: 500 11px sans-serif; fill: #374151; }
19637
20529
  .sx-matrix-offchart { fill: #ea580c; }
20530
+ .sx-sipoc-header { font: 700 13px sans-serif; fill: #fff; text-anchor: middle; dominant-baseline: central; }
20531
+ .sx-sipoc-headbox { stroke: #fff; stroke-width: 1; }
20532
+ .sx-sipoc-cell { fill: #fff; stroke: #cbd5e1; stroke-width: 1; }
20533
+ .sx-sipoc-cell-alt { fill: #f8fafc; stroke: #cbd5e1; stroke-width: 1; }
20534
+ .sx-sipoc-process { fill: #eff6ff; stroke: #cbd5e1; stroke-width: 1; }
20535
+ .sx-sipoc-item { font: 500 12px sans-serif; fill: #1f2937; text-anchor: middle; dominant-baseline: central; }
20536
+ .sx-sipoc-step { font: 600 12px sans-serif; fill: #1e3a8a; text-anchor: middle; dominant-baseline: central; }
20537
+ .sx-qfd-grid { stroke: #cbd5e1; stroke-width: 1; fill: none; }
20538
+ .sx-qfd-cellbg { fill: #fff; }
20539
+ .sx-qfd-cellbg-alt { fill: #f8fafc; }
20540
+ .sx-qfd-what { font: 500 12px sans-serif; fill: #1f2937; text-anchor: end; dominant-baseline: central; }
20541
+ .sx-qfd-how { font: 500 11.5px sans-serif; fill: #1f2937; }
20542
+ .sx-qfd-weight { font: 600 12px sans-serif; fill: #111; text-anchor: middle; dominant-baseline: central; }
20543
+ .sx-qfd-weight-head { font: 600 10px sans-serif; fill: #475569; text-anchor: middle; dominant-baseline: central; }
20544
+ .sx-qfd-rel-strong { fill: #2563eb; }
20545
+ .sx-qfd-rel-medium { fill: #93c5fd; stroke: #2563eb; stroke-width: 1.4; }
20546
+ .sx-qfd-rel-weak { fill: none; stroke: #2563eb; stroke-width: 1.4; }
20547
+ .sx-qfd-roof-cell { fill: #f8fafc; stroke: #94a3b8; stroke-width: 0.8; }
20548
+ .sx-qfd-roof-cell-filled { fill: #eef2ff; stroke: #64748b; stroke-width: 0.9; }
20549
+ .sx-qfd-corr { font: 700 13px sans-serif; text-anchor: middle; dominant-baseline: central; }
20550
+ .sx-qfd-corr-strong-pos { fill: #15803d; }
20551
+ .sx-qfd-corr-pos { fill: #16a34a; }
20552
+ .sx-qfd-corr-neg { fill: #dc2626; }
20553
+ .sx-qfd-corr-strong-neg { fill: #b91c1c; }
20554
+ .sx-qfd-imp-band { fill: #eff6ff; stroke: #cbd5e1; stroke-width: 1; }
20555
+ .sx-qfd-imp-head { font: 600 11px sans-serif; fill: #1e3a8a; text-anchor: end; dominant-baseline: central; }
20556
+ .sx-qfd-imp-value { font: 700 13px sans-serif; fill: #1e3a8a; text-anchor: middle; dominant-baseline: central; }
20557
+ .sx-qfd-imp-value-top { font: 800 14px sans-serif; fill: #dc2626; text-anchor: middle; dominant-baseline: central; }
20558
+ .sx-qfd-dir { font: 700 13px sans-serif; fill: #475569; text-anchor: middle; dominant-baseline: central; }
19638
20559
  `.trim();
19639
20560
  function axisArrow() {
19640
20561
  return el(
@@ -20421,7 +21342,339 @@ function renderTitle(ast, lay) {
20421
21342
  ast.title
20422
21343
  );
20423
21344
  }
21345
+ var SIPOC_COLUMN_DEFS = [
21346
+ { key: "suppliers", label: "Suppliers", color: "#2563eb" },
21347
+ { key: "inputs", label: "Inputs", color: "#0891b2" },
21348
+ { key: "process", label: "Process", color: "#1e3a8a" },
21349
+ { key: "outputs", label: "Outputs", color: "#0891b2" },
21350
+ { key: "customers", label: "Customers", color: "#2563eb" }
21351
+ ];
21352
+ function wrapToLines(textStr, maxChars, maxLines) {
21353
+ const lines = wrapLabel(textStr, maxChars);
21354
+ if (lines.length <= maxLines) return lines;
21355
+ const kept = lines.slice(0, maxLines);
21356
+ kept[maxLines - 1] = kept[maxLines - 1].replace(/\s+\S*$/, "") + "\u2026";
21357
+ return kept;
21358
+ }
21359
+ function renderSipocAST(ast) {
21360
+ const sipoc = ast.sipoc ?? { suppliers: [], inputs: [], process: [], outputs: [], customers: [] };
21361
+ const lay = layoutSipoc(ast);
21362
+ const nodes = [];
21363
+ if (ast.title) {
21364
+ nodes.push(
21365
+ text(
21366
+ { x: lay.canvasWidth / 2, y: 24, class: "sx-matrix-title", "text-anchor": "middle" },
21367
+ ast.title
21368
+ )
21369
+ );
21370
+ }
21371
+ const maxChars = Math.max(10, Math.floor((lay.colW - 16) / 6.4));
21372
+ SIPOC_COLUMN_DEFS.forEach((def, ci) => {
21373
+ const colX = lay.x0 + ci * lay.colW;
21374
+ nodes.push(
21375
+ rect({
21376
+ x: colX,
21377
+ y: lay.y0,
21378
+ width: lay.colW,
21379
+ height: lay.headerH,
21380
+ fill: def.color,
21381
+ class: "sx-sipoc-headbox"
21382
+ })
21383
+ );
21384
+ nodes.push(
21385
+ text(
21386
+ { x: colX + lay.colW / 2, y: lay.y0 + lay.headerH / 2, class: "sx-sipoc-header" },
21387
+ def.label
21388
+ )
21389
+ );
21390
+ const items = sipoc[def.key];
21391
+ const isProcess = def.key === "process";
21392
+ const cellNodes = [];
21393
+ for (let r5 = 0; r5 < lay.rows; r5++) {
21394
+ const cellY = lay.y0 + lay.headerH + r5 * lay.rowH;
21395
+ const item = items[r5];
21396
+ const cellClass = isProcess ? "sx-sipoc-process" : r5 % 2 === 0 ? "sx-sipoc-cell" : "sx-sipoc-cell-alt";
21397
+ cellNodes.push(
21398
+ rect({ x: colX, y: cellY, width: lay.colW, height: lay.rowH, class: cellClass })
21399
+ );
21400
+ if (item) {
21401
+ const label = isProcess ? `${r5 + 1}. ${item}` : item;
21402
+ const lines = wrapToLines(label, maxChars, 2);
21403
+ const lineH = 14;
21404
+ const startY = cellY + lay.rowH / 2 - (lines.length - 1) * lineH / 2;
21405
+ for (let i = 0; i < lines.length; i++) {
21406
+ cellNodes.push(
21407
+ text(
21408
+ {
21409
+ x: colX + lay.colW / 2,
21410
+ y: startY + i * lineH,
21411
+ class: isProcess ? "sx-sipoc-step" : "sx-sipoc-item"
21412
+ },
21413
+ lines[i]
21414
+ )
21415
+ );
21416
+ }
21417
+ }
21418
+ }
21419
+ nodes.push(
21420
+ group({ class: "sx-sipoc-column", "data-column": def.key }, [
21421
+ title(`${def.label}: ${items.join(", ")}`),
21422
+ ...cellNodes
21423
+ ])
21424
+ );
21425
+ });
21426
+ return svgRoot(
21427
+ {
21428
+ class: "sx-matrix sx-sipoc",
21429
+ "data-diagram-type": "matrix",
21430
+ "data-mode": "sipoc",
21431
+ width: lay.canvasWidth,
21432
+ height: lay.canvasHeight,
21433
+ viewBox: `0 0 ${lay.canvasWidth} ${lay.canvasHeight}`,
21434
+ role: "graphics-document"
21435
+ },
21436
+ [
21437
+ title(ast.title ? `SIPOC \u2014 ${escapeXml(ast.title)}` : "SIPOC diagram"),
21438
+ desc(
21439
+ `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)`
21440
+ ),
21441
+ defs([el("style", {}, CSS)]),
21442
+ ...nodes
21443
+ ]
21444
+ );
21445
+ }
21446
+ var CORR_GLYPH = {
21447
+ "++": "\u25CF",
21448
+ "+": "\u25CB",
21449
+ "-": "\u2212",
21450
+ "--": "\u2715"
21451
+ };
21452
+ var CORR_CLASS = {
21453
+ "++": "sx-qfd-corr-strong-pos",
21454
+ "+": "sx-qfd-corr-pos",
21455
+ "-": "sx-qfd-corr-neg",
21456
+ "--": "sx-qfd-corr-strong-neg"
21457
+ };
21458
+ var CORR_LABEL = {
21459
+ "++": "strong positive",
21460
+ "+": "positive",
21461
+ "-": "negative",
21462
+ "--": "strong negative"
21463
+ };
21464
+ function renderQfdRelationshipSymbol(strength, cx, cy, r5) {
21465
+ if (strength === 9) {
21466
+ return group({}, [
21467
+ circle({ cx, cy, r: r5, fill: "none", stroke: "#2563eb", "stroke-width": 1.4 }),
21468
+ circle({ cx, cy, r: r5 * 0.5, class: "sx-qfd-rel-strong" })
21469
+ ]);
21470
+ }
21471
+ if (strength === 3) {
21472
+ return circle({ cx, cy, r: r5, class: "sx-qfd-rel-medium" });
21473
+ }
21474
+ const t = r5 * 0.95;
21475
+ return polygon({
21476
+ points: `${cx},${cy - t} ${cx + t},${cy + t * 0.85} ${cx - t},${cy + t * 0.85}`,
21477
+ class: "sx-qfd-rel-weak"
21478
+ });
21479
+ }
21480
+ function renderQfdAST(ast) {
21481
+ const qfd = ast.qfd ?? { whats: [], hows: [], relationships: [], roof: [], normalize: false };
21482
+ const lay = layoutQfd(ast);
21483
+ const importance = computeQfdImportance(qfd);
21484
+ const maxImp = importance.reduce((m, c) => Math.max(m, c.importance), 0);
21485
+ const nodes = [];
21486
+ if (ast.title) {
21487
+ nodes.push(
21488
+ text(
21489
+ { x: lay.canvasWidth / 2, y: 24, class: "sx-matrix-title", "text-anchor": "middle" },
21490
+ ast.title
21491
+ )
21492
+ );
21493
+ }
21494
+ const gridRight = lay.gridX0 + lay.cols * lay.cellW;
21495
+ const gridBottom = lay.gridY0 + lay.rows * lay.cellH;
21496
+ const roofNodes = [];
21497
+ const half = lay.cellW / 2;
21498
+ const roofBaseY = lay.gridY0 - lay.howLabelH;
21499
+ const corrByPair = /* @__PURE__ */ new Map();
21500
+ for (const rc of qfd.roof) {
21501
+ const a = Math.min(rc.a, rc.b);
21502
+ const b = Math.max(rc.a, rc.b);
21503
+ corrByPair.set(`${a},${b}`, rc.correlation);
21504
+ }
21505
+ for (let i = 0; i < lay.cols; i++) {
21506
+ for (let j = i + 1; j < lay.cols; j++) {
21507
+ const depth = j - i;
21508
+ const cx = lay.gridX0 + ((i + j) / 2 + 0.5) * lay.cellW;
21509
+ const cy = roofBaseY - (depth - 0.5) * half;
21510
+ const corr = corrByPair.get(`${i},${j}`);
21511
+ const diamond = polygon({
21512
+ points: `${cx},${cy - half} ${cx + half},${cy} ${cx},${cy + half} ${cx - half},${cy}`,
21513
+ class: corr ? "sx-qfd-roof-cell-filled" : "sx-qfd-roof-cell"
21514
+ });
21515
+ const cellChildren = [
21516
+ title(
21517
+ corr ? `${qfd.hows[i]?.label ?? `HOW ${i}`} \u2194 ${qfd.hows[j]?.label ?? `HOW ${j}`}: ${CORR_LABEL[corr]}` : `${qfd.hows[i]?.label ?? `HOW ${i}`} \u2194 ${qfd.hows[j]?.label ?? `HOW ${j}`}: no correlation`
21518
+ ),
21519
+ diamond
21520
+ ];
21521
+ if (corr) {
21522
+ cellChildren.push(
21523
+ text(
21524
+ { x: cx, y: cy, class: `sx-qfd-corr ${CORR_CLASS[corr]}` },
21525
+ CORR_GLYPH[corr]
21526
+ )
21527
+ );
21528
+ }
21529
+ roofNodes.push(
21530
+ group(
21531
+ {
21532
+ class: "sx-qfd-roof-pair",
21533
+ "data-pair": `${i},${j}`,
21534
+ ...corr ? { "data-corr": corr } : {}
21535
+ },
21536
+ cellChildren
21537
+ )
21538
+ );
21539
+ }
21540
+ }
21541
+ nodes.push(group({ class: "sx-qfd-roof" }, [title("Roof: engineering correlation matrix"), ...roofNodes]));
21542
+ qfd.hows.forEach((how, ci) => {
21543
+ const cx = lay.gridX0 + (ci + 0.5) * lay.cellW;
21544
+ const baseY = lay.gridY0 - 8;
21545
+ nodes.push(
21546
+ text(
21547
+ {
21548
+ x: cx,
21549
+ y: baseY,
21550
+ class: "sx-qfd-how",
21551
+ "text-anchor": "start",
21552
+ transform: `rotate(-60 ${cx} ${baseY})`
21553
+ },
21554
+ how.label
21555
+ )
21556
+ );
21557
+ if (how.direction) {
21558
+ const glyph = how.direction === "up" ? "\u25B2" : how.direction === "down" ? "\u25BC" : "\u25C7";
21559
+ nodes.push(
21560
+ text({ x: cx, y: lay.gridY0 - 4, class: "sx-qfd-dir" }, glyph)
21561
+ );
21562
+ }
21563
+ });
21564
+ nodes.push(
21565
+ text(
21566
+ { x: lay.gridX0 - lay.weightW / 2, y: lay.gridY0 - 8, class: "sx-qfd-weight-head" },
21567
+ "Wt"
21568
+ )
21569
+ );
21570
+ const gridNodes = [];
21571
+ for (let r5 = 0; r5 < lay.rows; r5++) {
21572
+ const y = lay.gridY0 + r5 * lay.cellH;
21573
+ gridNodes.push(
21574
+ rect({
21575
+ x: lay.gridX0,
21576
+ y,
21577
+ width: lay.cols * lay.cellW,
21578
+ height: lay.cellH,
21579
+ class: r5 % 2 === 0 ? "sx-qfd-cellbg" : "sx-qfd-cellbg-alt"
21580
+ })
21581
+ );
21582
+ }
21583
+ for (let i = 0; i <= lay.cols; i++) {
21584
+ const x = lay.gridX0 + i * lay.cellW;
21585
+ gridNodes.push(line({ x1: x, y1: lay.gridY0, x2: x, y2: gridBottom, class: "sx-qfd-grid" }));
21586
+ }
21587
+ for (let j = 0; j <= lay.rows; j++) {
21588
+ const y = lay.gridY0 + j * lay.cellH;
21589
+ gridNodes.push(line({ x1: lay.gridX0, y1: y, x2: gridRight, y2: y, class: "sx-qfd-grid" }));
21590
+ }
21591
+ nodes.push(group({ class: "sx-qfd-gridlines" }, gridNodes));
21592
+ const whatMaxChars = Math.max(10, Math.floor((lay.whatLabelW - 12) / 6.4));
21593
+ qfd.whats.forEach((what, ri) => {
21594
+ const cy = lay.gridY0 + (ri + 0.5) * lay.cellH;
21595
+ const lines = wrapToLines(what.label, whatMaxChars, 2);
21596
+ const lineH = 13;
21597
+ const startY = cy - (lines.length - 1) * lineH / 2;
21598
+ const labelNodes = lines.map(
21599
+ (ln, i) => text({ x: lay.gridX0 - lay.weightW - 8, y: startY + i * lineH, class: "sx-qfd-what" }, ln)
21600
+ );
21601
+ labelNodes.push(
21602
+ text({ x: lay.gridX0 - lay.weightW / 2, y: cy, class: "sx-qfd-weight" }, String(what.weight))
21603
+ );
21604
+ nodes.push(
21605
+ group({ class: "sx-qfd-what-row", "data-what": String(ri) }, [
21606
+ title(`${what.label} (weight ${what.weight})`),
21607
+ ...labelNodes
21608
+ ])
21609
+ );
21610
+ });
21611
+ const relNodes = [];
21612
+ const symR = Math.min(lay.cellW, lay.cellH) * 0.3;
21613
+ for (const rel of qfd.relationships) {
21614
+ if (rel.how < 0 || rel.how >= lay.cols || rel.what < 0 || rel.what >= lay.rows) continue;
21615
+ const cx = lay.gridX0 + (rel.how + 0.5) * lay.cellW;
21616
+ const cy = lay.gridY0 + (rel.what + 0.5) * lay.cellH;
21617
+ relNodes.push(
21618
+ group(
21619
+ { class: "sx-qfd-rel", "data-strength": String(rel.strength) },
21620
+ [
21621
+ title(`${qfd.whats[rel.what]?.label ?? ""} \xD7 ${qfd.hows[rel.how]?.label ?? ""} = ${rel.strength}`),
21622
+ renderQfdRelationshipSymbol(rel.strength, cx, cy, symR)
21623
+ ]
21624
+ )
21625
+ );
21626
+ }
21627
+ nodes.push(group({ class: "sx-qfd-relationships" }, relNodes));
21628
+ const footerNodes = [];
21629
+ const impRowY = gridBottom;
21630
+ const impH = lay.footerH;
21631
+ footerNodes.push(
21632
+ rect({ x: lay.gridX0, y: impRowY, width: lay.cols * lay.cellW, height: impH, class: "sx-qfd-imp-band" })
21633
+ );
21634
+ for (let i = 1; i < lay.cols; i++) {
21635
+ const x = lay.gridX0 + i * lay.cellW;
21636
+ footerNodes.push(line({ x1: x, y1: impRowY, x2: x, y2: impRowY + impH, class: "sx-qfd-grid" }));
21637
+ }
21638
+ const footerLabel = qfd.normalize ? "Importance %" : "Technical importance \u03A3(wt\xD7rel)";
21639
+ footerNodes.push(
21640
+ text({ x: lay.gridX0 - 8, y: impRowY + impH / 2, class: "sx-qfd-imp-head" }, footerLabel)
21641
+ );
21642
+ importance.forEach((col) => {
21643
+ const cx = lay.gridX0 + (col.how + 0.5) * lay.cellW;
21644
+ const cy = impRowY + impH / 2;
21645
+ const isTop = col.importance === maxImp && maxImp > 0;
21646
+ const display = qfd.normalize ? `${col.percent}%` : String(col.importance);
21647
+ footerNodes.push(
21648
+ text(
21649
+ { x: cx, y: cy, class: isTop ? "sx-qfd-imp-value-top" : "sx-qfd-imp-value" },
21650
+ display
21651
+ )
21652
+ );
21653
+ });
21654
+ nodes.push(group({ class: "sx-qfd-importance" }, [title("Computed technical importance per engineering characteristic"), ...footerNodes]));
21655
+ return svgRoot(
21656
+ {
21657
+ class: "sx-matrix sx-qfd",
21658
+ "data-diagram-type": "matrix",
21659
+ "data-mode": "qfd",
21660
+ width: lay.canvasWidth,
21661
+ height: lay.canvasHeight,
21662
+ viewBox: `0 0 ${lay.canvasWidth} ${lay.canvasHeight}`,
21663
+ role: "graphics-document"
21664
+ },
21665
+ [
21666
+ title(ast.title ? `QFD House of Quality \u2014 ${escapeXml(ast.title)}` : "QFD House of Quality"),
21667
+ desc(
21668
+ `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`
21669
+ ),
21670
+ defs([el("style", {}, CSS)]),
21671
+ ...nodes
21672
+ ]
21673
+ );
21674
+ }
20424
21675
  function renderMatrixAST(ast) {
21676
+ if (ast.mode === "sipoc") return renderSipocAST(ast);
21677
+ if (ast.mode === "qfd") return renderQfdAST(ast);
20425
21678
  const lay = layoutMatrix(ast);
20426
21679
  const needsLegendSpace = lay.categories.length > 0 || ast.mode === "correlation";
20427
21680
  const extraWidth = needsLegendSpace && lay.plot.x0 + lay.plot.w + 140 > lay.canvasWidth ? 160 : 0;
@@ -20501,7 +21754,7 @@ function stripComment6(s) {
20501
21754
  }
20502
21755
  return out;
20503
21756
  }
20504
- function unquote5(v) {
21757
+ function unquote6(v) {
20505
21758
  const t = v.trim();
20506
21759
  if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
20507
21760
  return t;
@@ -20590,7 +21843,7 @@ function parseErd(text2) {
20590
21843
  continue;
20591
21844
  }
20592
21845
  if (lower.startsWith("title:")) {
20593
- title2 = unquote5(t.slice("title:".length).trim());
21846
+ title2 = unquote6(t.slice("title:".length).trim());
20594
21847
  i++;
20595
21848
  continue;
20596
21849
  }
@@ -20790,7 +22043,7 @@ function parseAttributeLine(raw, lineNumber, tableId) {
20790
22043
  let comment;
20791
22044
  const colonIdx = findUnquotedChar(s, ":");
20792
22045
  if (colonIdx >= 0) {
20793
- comment = unquote5(s.slice(colonIdx + 1).trim());
22046
+ comment = unquote6(s.slice(colonIdx + 1).trim());
20794
22047
  s = s.slice(0, colonIdx).trim();
20795
22048
  }
20796
22049
  let fkTarget;
@@ -20842,7 +22095,7 @@ function parseRefLine(raw, lineNumber, outRefs) {
20842
22095
  let label;
20843
22096
  const colonIdx = findUnquotedChar(s, ":");
20844
22097
  if (colonIdx >= 0) {
20845
- label = unquote5(s.slice(colonIdx + 1).trim());
22098
+ label = unquote6(s.slice(colonIdx + 1).trim());
20846
22099
  s = s.slice(0, colonIdx).trim();
20847
22100
  }
20848
22101
  const merm = /^(\S+)\s+([}|o][o|]|\|\||\|o)(\.\.|--|~~)([}|o][{|]|\|\||o\|)\s+(\S+)$/.exec(s);
@@ -21291,7 +22544,7 @@ function sideAnchor(e, side, col) {
21291
22544
  }
21292
22545
 
21293
22546
  // src/diagrams/erd/renderer.ts
21294
- function buildCss8(t) {
22547
+ function buildCss9(t) {
21295
22548
  return `
21296
22549
  .lt-erd { font-family: system-ui, -apple-system, sans-serif; }
21297
22550
  .lt-erd-title { font: bold 16px sans-serif; fill: ${t.text}; }
@@ -21601,7 +22854,7 @@ function renderEdge5(edge) {
21601
22854
  function renderErdAst(result, themeName = "default") {
21602
22855
  const theme = resolveBaseTheme(themeName);
21603
22856
  const { entities, edges, width, height, ast } = result;
21604
- const cssBlock = el("style", {}, buildCss8(theme));
22857
+ const cssBlock = el("style", {}, buildCss9(theme));
21605
22858
  const titleNode = title(ast.title ?? "Schematex ERD");
21606
22859
  const descNode = desc(
21607
22860
  `Entity-Relationship Diagram with ${entities.length} entities and ${edges.length} relationships.`
@@ -21682,7 +22935,7 @@ function stripComment7(s) {
21682
22935
  }
21683
22936
  return out;
21684
22937
  }
21685
- function unquote6(v) {
22938
+ function unquote7(v) {
21686
22939
  const t = v.trim();
21687
22940
  if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
21688
22941
  return t;
@@ -21924,7 +23177,7 @@ function parseBreadboard(text2) {
21924
23177
  continue;
21925
23178
  }
21926
23179
  if (lower.startsWith("title:")) {
21927
- title2 = unquote6(line2.text.slice("title:".length).trim());
23180
+ title2 = unquote7(line2.text.slice("title:".length).trim());
21928
23181
  i++;
21929
23182
  continue;
21930
23183
  }
@@ -22671,7 +23924,7 @@ var WIRE_COLOR_MAP = {
22671
23924
  brown: "#78350f",
22672
23925
  grey: "#64748b"
22673
23926
  };
22674
- function buildCss9(t) {
23927
+ function buildCss10(t) {
22675
23928
  return `
22676
23929
  .lt-bb { font-family: system-ui, -apple-system, sans-serif; }
22677
23930
  .lt-bb-title { font: 600 16px sans-serif; fill: ${t.text}; }
@@ -22819,7 +24072,7 @@ function renderWire(lw) {
22819
24072
  }
22820
24073
  function renderBreadboardLayout(layout, config) {
22821
24074
  const theme = resolveBaseTheme(config?.theme ?? "default");
22822
- const css = buildCss9(theme);
24075
+ const css = buildCss10(theme);
22823
24076
  const titleStr = layout.ast.title ?? "Breadboard";
22824
24077
  const titleNode = layout.ast.title ? text({ x: BB_CONST.MARGIN, y: 22, class: "lt-bb-title" }, layout.ast.title) : "";
22825
24078
  const substrate = renderSubstrate(layout.substrate);
@@ -22878,7 +24131,7 @@ var BpmnParseError = class extends Error {
22878
24131
  }
22879
24132
  line;
22880
24133
  };
22881
- function unquote7(s) {
24134
+ function unquote8(s) {
22882
24135
  return s.replace(/^"|"$/g, "").replace(/\\"/g, '"');
22883
24136
  }
22884
24137
  function takeQuoted(rest) {
@@ -23048,7 +24301,7 @@ function parsePool(lines, startIdx, pools, lanesById, events, activities, gatewa
23048
24301
  const ln = lines[startIdx];
23049
24302
  const m = ln.text.match(/^pool\s+("(?:\\.|[^"\\])*")\s*(.*)$/);
23050
24303
  if (!m) throw new BpmnParseError("malformed pool declaration", ln.no);
23051
- const label = unquote7(m[1]);
24304
+ const label = unquote8(m[1]);
23052
24305
  const tail = m[2].trim();
23053
24306
  let blackbox = false;
23054
24307
  let hasBrace = false;
@@ -23100,7 +24353,7 @@ function parseLane(lines, startIdx, pool, lanesById, events, activities, gateway
23100
24353
  const ln = lines[startIdx];
23101
24354
  const m = ln.text.match(/^lane\s+("(?:\\.|[^"\\])*")\s*\{?\s*$/);
23102
24355
  if (!m) throw new BpmnParseError("malformed lane declaration", ln.no);
23103
- const label = unquote7(m[1]);
24356
+ const label = unquote8(m[1]);
23104
24357
  const hasBrace = ln.text.endsWith("{");
23105
24358
  if (pool.blackbox) {
23106
24359
  throw new BpmnParseError(
@@ -25185,7 +26438,7 @@ function buildGraph2(network2) {
25185
26438
  }
25186
26439
  return { incoming, outgoing, inputVars, outputVars };
25187
26440
  }
25188
- function assignLayers(network2, graph) {
26441
+ function assignLayers2(network2, graph) {
25189
26442
  const layer = /* @__PURE__ */ new Map();
25190
26443
  const visiting = /* @__PURE__ */ new Set();
25191
26444
  const visit = (id) => {
@@ -25209,7 +26462,7 @@ function layoutNetwork3(network2, originX, originY) {
25209
26462
  const graph = buildGraph2(network2);
25210
26463
  const sizes = /* @__PURE__ */ new Map();
25211
26464
  for (const b of network2.blocks) sizes.set(b.id, computeBlockSize(b));
25212
- const layerOf = assignLayers(network2, graph);
26465
+ const layerOf = assignLayers2(network2, graph);
25213
26466
  const maxLayer = Math.max(0, ...Array.from(layerOf.values()));
25214
26467
  const byLayer = Array.from({ length: maxLayer + 1 }, () => []);
25215
26468
  for (const b of network2.blocks) byLayer[layerOf.get(b.id)].push(b.id);
@@ -26646,7 +27899,7 @@ function normalizeHeader(text2, type) {
26646
27899
  }
26647
27900
  return text2;
26648
27901
  }
26649
- function preprocess8(text2) {
27902
+ function preprocess9(text2) {
26650
27903
  const { data, body } = parseFrontmatter(stripCodeFences(text2));
26651
27904
  if (!data.title) return body;
26652
27905
  const safeTitle = data.title.replace(/"/g, '\\"');
@@ -26661,7 +27914,7 @@ function preprocess8(text2) {
26661
27914
  return body;
26662
27915
  }
26663
27916
  function parse(text2, config) {
26664
- const prepared0 = preprocess8(text2);
27917
+ const prepared0 = preprocess9(text2);
26665
27918
  const plugin = detectPlugin(prepared0, config);
26666
27919
  const prepared = normalizeHeader(prepared0, plugin.type);
26667
27920
  if (plugin.parse) return plugin.parse(prepared);
@@ -26672,7 +27925,7 @@ function parse(text2, config) {
26672
27925
  function parseResult(text2, config) {
26673
27926
  let plugin;
26674
27927
  try {
26675
- const prepared0 = preprocess8(text2);
27928
+ const prepared0 = preprocess9(text2);
26676
27929
  plugin = detectPlugin(prepared0, config);
26677
27930
  if (!plugin.parse) {
26678
27931
  throw new Error(
@@ -26708,7 +27961,7 @@ function runLint(plugin, prepared) {
26708
27961
  }
26709
27962
  function render(text2, config) {
26710
27963
  if (config?.mode === "preview") return renderResult(text2, config).svg;
26711
- const prepared0 = preprocess8(text2);
27964
+ const prepared0 = preprocess9(text2);
26712
27965
  const plugin = detectPlugin(prepared0, config);
26713
27966
  const prepared = normalizeHeader(prepared0, plugin.type);
26714
27967
  return renderWithPlugin(prepared, plugin, config);
@@ -26716,7 +27969,7 @@ function render(text2, config) {
26716
27969
  function renderResult(text2, config) {
26717
27970
  let plugin;
26718
27971
  try {
26719
- const prepared0 = preprocess8(text2);
27972
+ const prepared0 = preprocess9(text2);
26720
27973
  plugin = detectPlugin(prepared0, config);
26721
27974
  const prepared = normalizeHeader(prepared0, plugin.type);
26722
27975
  const svg = renderWithPlugin(prepared, plugin, config);
@@ -26756,5 +28009,5 @@ function renderWithPlugin(prepared, plugin, config) {
26756
28009
  }
26757
28010
 
26758
28011
  export { GEOMETRY, bowtie2 as bowtie, decisiontree, drawDeviceIcon, faulttree, iconSize, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, timeline, umlclass, usecase };
26759
- //# sourceMappingURL=chunk-6AWASOFO.js.map
26760
- //# sourceMappingURL=chunk-6AWASOFO.js.map
28012
+ //# sourceMappingURL=chunk-IFNNV54X.js.map
28013
+ //# sourceMappingURL=chunk-IFNNV54X.js.map