schematex 0.1.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 (181) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +379 -0
  3. package/dist/chunk-2MQWZ2XY.cjs +453 -0
  4. package/dist/chunk-2MQWZ2XY.cjs.map +1 -0
  5. package/dist/chunk-2UKC6ZCY.cjs +1803 -0
  6. package/dist/chunk-2UKC6ZCY.cjs.map +1 -0
  7. package/dist/chunk-34X3ZJ6E.cjs +783 -0
  8. package/dist/chunk-34X3ZJ6E.cjs.map +1 -0
  9. package/dist/chunk-3FTUWAXK.cjs +1220 -0
  10. package/dist/chunk-3FTUWAXK.cjs.map +1 -0
  11. package/dist/chunk-3J7TFUOC.js +745 -0
  12. package/dist/chunk-3J7TFUOC.js.map +1 -0
  13. package/dist/chunk-47ZC6EMJ.js +1009 -0
  14. package/dist/chunk-47ZC6EMJ.js.map +1 -0
  15. package/dist/chunk-4DBRNOPA.cjs +750 -0
  16. package/dist/chunk-4DBRNOPA.cjs.map +1 -0
  17. package/dist/chunk-4G7ZIBHN.js +778 -0
  18. package/dist/chunk-4G7ZIBHN.js.map +1 -0
  19. package/dist/chunk-5C7DPDHQ.js +1321 -0
  20. package/dist/chunk-5C7DPDHQ.js.map +1 -0
  21. package/dist/chunk-ADOXGKAK.js +1251 -0
  22. package/dist/chunk-ADOXGKAK.js.map +1 -0
  23. package/dist/chunk-BE5HNDA5.cjs +874 -0
  24. package/dist/chunk-BE5HNDA5.cjs.map +1 -0
  25. package/dist/chunk-CZRM7LT7.js +889 -0
  26. package/dist/chunk-CZRM7LT7.js.map +1 -0
  27. package/dist/chunk-D4JTSPOL.js +1795 -0
  28. package/dist/chunk-D4JTSPOL.js.map +1 -0
  29. package/dist/chunk-DS47NTWZ.cjs +1034 -0
  30. package/dist/chunk-DS47NTWZ.cjs.map +1 -0
  31. package/dist/chunk-FDLZEKEB.js +449 -0
  32. package/dist/chunk-FDLZEKEB.js.map +1 -0
  33. package/dist/chunk-FGPTCDUT.cjs +1851 -0
  34. package/dist/chunk-FGPTCDUT.cjs.map +1 -0
  35. package/dist/chunk-HDKDQAEQ.cjs +86 -0
  36. package/dist/chunk-HDKDQAEQ.cjs.map +1 -0
  37. package/dist/chunk-IX554O5K.js +346 -0
  38. package/dist/chunk-IX554O5K.js.map +1 -0
  39. package/dist/chunk-KLJEK547.js +71 -0
  40. package/dist/chunk-KLJEK547.js.map +1 -0
  41. package/dist/chunk-LMFSHK45.js +1028 -0
  42. package/dist/chunk-LMFSHK45.js.map +1 -0
  43. package/dist/chunk-MDICUK6F.cjs +1258 -0
  44. package/dist/chunk-MDICUK6F.cjs.map +1 -0
  45. package/dist/chunk-N7KOXOMX.cjs +363 -0
  46. package/dist/chunk-N7KOXOMX.cjs.map +1 -0
  47. package/dist/chunk-NYCIK4SU.cjs +775 -0
  48. package/dist/chunk-NYCIK4SU.cjs.map +1 -0
  49. package/dist/chunk-PDPHRZZT.js +770 -0
  50. package/dist/chunk-PDPHRZZT.js.map +1 -0
  51. package/dist/chunk-ROFLJ74T.js +1212 -0
  52. package/dist/chunk-ROFLJ74T.js.map +1 -0
  53. package/dist/chunk-S6BK5DB6.cjs +845 -0
  54. package/dist/chunk-S6BK5DB6.cjs.map +1 -0
  55. package/dist/chunk-U4I37IBN.js +874 -0
  56. package/dist/chunk-U4I37IBN.js.map +1 -0
  57. package/dist/chunk-U5GGE6PJ.js +839 -0
  58. package/dist/chunk-U5GGE6PJ.js.map +1 -0
  59. package/dist/chunk-UHLYS3W5.cjs +1015 -0
  60. package/dist/chunk-UHLYS3W5.cjs.map +1 -0
  61. package/dist/chunk-URSKIHSY.cjs +881 -0
  62. package/dist/chunk-URSKIHSY.cjs.map +1 -0
  63. package/dist/chunk-V6WO7RK7.cjs +1056 -0
  64. package/dist/chunk-V6WO7RK7.cjs.map +1 -0
  65. package/dist/chunk-VFQCTXOX.js +869 -0
  66. package/dist/chunk-VFQCTXOX.js.map +1 -0
  67. package/dist/chunk-XQ52ICHU.cjs +895 -0
  68. package/dist/chunk-XQ52ICHU.cjs.map +1 -0
  69. package/dist/chunk-XX4BKS7Y.js +1051 -0
  70. package/dist/chunk-XX4BKS7Y.js.map +1 -0
  71. package/dist/chunk-XXU36667.js +1844 -0
  72. package/dist/chunk-XXU36667.js.map +1 -0
  73. package/dist/chunk-ZX7QKZK2.cjs +1326 -0
  74. package/dist/chunk-ZX7QKZK2.cjs.map +1 -0
  75. package/dist/diagrams/blockdiagram/index.cjs +25 -0
  76. package/dist/diagrams/blockdiagram/index.cjs.map +1 -0
  77. package/dist/diagrams/blockdiagram/index.d.cts +67 -0
  78. package/dist/diagrams/blockdiagram/index.d.ts +67 -0
  79. package/dist/diagrams/blockdiagram/index.js +4 -0
  80. package/dist/diagrams/blockdiagram/index.js.map +1 -0
  81. package/dist/diagrams/circuit/index.cjs +34 -0
  82. package/dist/diagrams/circuit/index.cjs.map +1 -0
  83. package/dist/diagrams/circuit/index.d.cts +138 -0
  84. package/dist/diagrams/circuit/index.d.ts +138 -0
  85. package/dist/diagrams/circuit/index.js +5 -0
  86. package/dist/diagrams/circuit/index.js.map +1 -0
  87. package/dist/diagrams/ecomap/index.cjs +30 -0
  88. package/dist/diagrams/ecomap/index.cjs.map +1 -0
  89. package/dist/diagrams/ecomap/index.d.cts +15 -0
  90. package/dist/diagrams/ecomap/index.d.ts +15 -0
  91. package/dist/diagrams/ecomap/index.js +5 -0
  92. package/dist/diagrams/ecomap/index.js.map +1 -0
  93. package/dist/diagrams/entity/index.cjs +26 -0
  94. package/dist/diagrams/entity/index.cjs.map +1 -0
  95. package/dist/diagrams/entity/index.d.cts +54 -0
  96. package/dist/diagrams/entity/index.d.ts +54 -0
  97. package/dist/diagrams/entity/index.js +5 -0
  98. package/dist/diagrams/entity/index.js.map +1 -0
  99. package/dist/diagrams/fishbone/index.cjs +34 -0
  100. package/dist/diagrams/fishbone/index.cjs.map +1 -0
  101. package/dist/diagrams/fishbone/index.d.cts +185 -0
  102. package/dist/diagrams/fishbone/index.d.ts +185 -0
  103. package/dist/diagrams/fishbone/index.js +5 -0
  104. package/dist/diagrams/fishbone/index.js.map +1 -0
  105. package/dist/diagrams/flowchart/index.cjs +34 -0
  106. package/dist/diagrams/flowchart/index.cjs.map +1 -0
  107. package/dist/diagrams/flowchart/index.d.cts +2 -0
  108. package/dist/diagrams/flowchart/index.d.ts +2 -0
  109. package/dist/diagrams/flowchart/index.js +5 -0
  110. package/dist/diagrams/flowchart/index.js.map +1 -0
  111. package/dist/diagrams/genogram/index.cjs +38 -0
  112. package/dist/diagrams/genogram/index.cjs.map +1 -0
  113. package/dist/diagrams/genogram/index.d.cts +20 -0
  114. package/dist/diagrams/genogram/index.d.ts +20 -0
  115. package/dist/diagrams/genogram/index.js +5 -0
  116. package/dist/diagrams/genogram/index.js.map +1 -0
  117. package/dist/diagrams/ladder/index.cjs +26 -0
  118. package/dist/diagrams/ladder/index.cjs.map +1 -0
  119. package/dist/diagrams/ladder/index.d.cts +49 -0
  120. package/dist/diagrams/ladder/index.d.ts +49 -0
  121. package/dist/diagrams/ladder/index.js +5 -0
  122. package/dist/diagrams/ladder/index.js.map +1 -0
  123. package/dist/diagrams/logic/index.cjs +26 -0
  124. package/dist/diagrams/logic/index.cjs.map +1 -0
  125. package/dist/diagrams/logic/index.d.cts +73 -0
  126. package/dist/diagrams/logic/index.d.ts +73 -0
  127. package/dist/diagrams/logic/index.js +5 -0
  128. package/dist/diagrams/logic/index.js.map +1 -0
  129. package/dist/diagrams/orgchart/index.cjs +30 -0
  130. package/dist/diagrams/orgchart/index.cjs.map +1 -0
  131. package/dist/diagrams/orgchart/index.d.cts +100 -0
  132. package/dist/diagrams/orgchart/index.d.ts +100 -0
  133. package/dist/diagrams/orgchart/index.js +5 -0
  134. package/dist/diagrams/orgchart/index.js.map +1 -0
  135. package/dist/diagrams/pedigree/index.cjs +30 -0
  136. package/dist/diagrams/pedigree/index.cjs.map +1 -0
  137. package/dist/diagrams/pedigree/index.d.cts +15 -0
  138. package/dist/diagrams/pedigree/index.d.ts +15 -0
  139. package/dist/diagrams/pedigree/index.js +5 -0
  140. package/dist/diagrams/pedigree/index.js.map +1 -0
  141. package/dist/diagrams/phylo/index.cjs +30 -0
  142. package/dist/diagrams/phylo/index.cjs.map +1 -0
  143. package/dist/diagrams/phylo/index.d.cts +32 -0
  144. package/dist/diagrams/phylo/index.d.ts +32 -0
  145. package/dist/diagrams/phylo/index.js +5 -0
  146. package/dist/diagrams/phylo/index.js.map +1 -0
  147. package/dist/diagrams/sld/index.cjs +26 -0
  148. package/dist/diagrams/sld/index.cjs.map +1 -0
  149. package/dist/diagrams/sld/index.d.cts +58 -0
  150. package/dist/diagrams/sld/index.d.ts +58 -0
  151. package/dist/diagrams/sld/index.js +5 -0
  152. package/dist/diagrams/sld/index.js.map +1 -0
  153. package/dist/diagrams/sociogram/index.cjs +26 -0
  154. package/dist/diagrams/sociogram/index.cjs.map +1 -0
  155. package/dist/diagrams/sociogram/index.d.cts +76 -0
  156. package/dist/diagrams/sociogram/index.d.ts +76 -0
  157. package/dist/diagrams/sociogram/index.js +5 -0
  158. package/dist/diagrams/sociogram/index.js.map +1 -0
  159. package/dist/diagrams/timing/index.cjs +21 -0
  160. package/dist/diagrams/timing/index.cjs.map +1 -0
  161. package/dist/diagrams/timing/index.d.cts +9 -0
  162. package/dist/diagrams/timing/index.d.ts +9 -0
  163. package/dist/diagrams/timing/index.js +4 -0
  164. package/dist/diagrams/timing/index.js.map +1 -0
  165. package/dist/diagrams/venn/index.cjs +38 -0
  166. package/dist/diagrams/venn/index.cjs.map +1 -0
  167. package/dist/diagrams/venn/index.d.cts +69 -0
  168. package/dist/diagrams/venn/index.d.ts +69 -0
  169. package/dist/diagrams/venn/index.js +5 -0
  170. package/dist/diagrams/venn/index.js.map +1 -0
  171. package/dist/index-BSlza1YY.d.ts +150 -0
  172. package/dist/index-BXefHVce.d.cts +150 -0
  173. package/dist/index.cjs +2033 -0
  174. package/dist/index.cjs.map +1 -0
  175. package/dist/index.d.cts +29 -0
  176. package/dist/index.d.ts +29 -0
  177. package/dist/index.js +1944 -0
  178. package/dist/index.js.map +1 -0
  179. package/dist/types-DqfcYkcY.d.cts +741 -0
  180. package/dist/types-DqfcYkcY.d.ts +741 -0
  181. package/package.json +163 -0
@@ -0,0 +1,839 @@
1
+ import { resolveBaseTheme, STROKE_WIDTH, cssCustomProperties } from './chunk-IX554O5K.js';
2
+ import { svgRoot, title, desc, el, text, group } from './chunk-KLJEK547.js';
3
+
4
+ // src/diagrams/ecomap/parser.ts
5
+ var EcomapParseError = class extends Error {
6
+ constructor(message, line) {
7
+ super(`Line ${line}: ${message}`);
8
+ this.line = line;
9
+ this.name = "EcomapParseError";
10
+ }
11
+ line;
12
+ };
13
+ var CONNECTION_OPS = {
14
+ "===": { relType: "strong" },
15
+ "==": { relType: "moderate" },
16
+ "---": { relType: "normal" },
17
+ "- -": { relType: "weak" },
18
+ "~~~": { relType: "stressful" },
19
+ "~=~": { relType: "stressful-strong" },
20
+ "~x~": { relType: "conflictual" },
21
+ "-/-": { relType: "broken" },
22
+ "===>": { relType: "strong", energyFlow: "from" },
23
+ "<===": { relType: "strong", energyFlow: "to" },
24
+ "<=>": { relType: "moderate", energyFlow: "mutual" },
25
+ "==>": { relType: "moderate", energyFlow: "from" },
26
+ "<==": { relType: "moderate", energyFlow: "to" },
27
+ "-->": { relType: "normal", energyFlow: "from" },
28
+ "<--": { relType: "normal", energyFlow: "to" },
29
+ "<->": { relType: "normal", energyFlow: "mutual" }
30
+ };
31
+ var OP_PATTERN = "===>|<===|===|<=>|==>|<==|<->|-->|<--|~=~|~x~|~~~|-\\/-|---|==|- -";
32
+ var CONNECTION_RE = new RegExp(
33
+ `^([a-zA-Z][a-zA-Z0-9_-]*)\\s+(${OP_PATTERN})\\s+([a-zA-Z][a-zA-Z0-9_-]*)(.*)$`
34
+ );
35
+ function parseEcomap(text2) {
36
+ const lines = text2.split("\n");
37
+ const individuals = [];
38
+ const knownIds = /* @__PURE__ */ new Set();
39
+ const relationships = [];
40
+ const metadata = {};
41
+ let centerId = null;
42
+ let i = 0;
43
+ while (i < lines.length && lines[i].trim() === "") i++;
44
+ if (i >= lines.length) throw new EcomapParseError("Empty input", 1);
45
+ const header = lines[i].trim();
46
+ const headerMatch = header.match(/^ecomap\s*(?:"([^"]*)")?$/i);
47
+ if (!headerMatch)
48
+ throw new EcomapParseError("Expected 'ecomap' header", i + 1);
49
+ if (headerMatch[1]) metadata.title = headerMatch[1];
50
+ i++;
51
+ while (i < lines.length) {
52
+ const raw = lines[i];
53
+ const trimmed = raw.trim();
54
+ if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith("//")) {
55
+ i++;
56
+ continue;
57
+ }
58
+ const centerMatch = trimmed.match(
59
+ /^center:\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*(\[.*\])?\s*$/
60
+ );
61
+ if (centerMatch) {
62
+ const id = centerMatch[1].toLowerCase();
63
+ centerId = id;
64
+ const props = centerMatch[2] ? parseProps(centerMatch[2]) : {};
65
+ individuals.push(buildIndividual(id, props, true));
66
+ knownIds.add(id);
67
+ i++;
68
+ const baseIndent = leadingSpaces(raw);
69
+ while (i < lines.length) {
70
+ if (lines[i].trim() === "") {
71
+ i++;
72
+ continue;
73
+ }
74
+ if (leadingSpaces(lines[i]) > baseIndent + 2) {
75
+ i++;
76
+ } else {
77
+ break;
78
+ }
79
+ }
80
+ continue;
81
+ }
82
+ const connMatch = trimmed.match(CONNECTION_RE);
83
+ if (connMatch) {
84
+ const fromRaw = connMatch[1].toLowerCase();
85
+ const op = connMatch[2];
86
+ const toRaw = connMatch[3].toLowerCase();
87
+ const rest = connMatch[4]?.trim() ?? "";
88
+ const opInfo = CONNECTION_OPS[op];
89
+ if (!opInfo)
90
+ throw new EcomapParseError(`Unknown operator '${op}'`, i + 1);
91
+ let label;
92
+ const lblMatch = rest.match(/\[\s*label:\s*"([^"]*)"\s*\]/);
93
+ if (lblMatch) label = lblMatch[1];
94
+ let from = fromRaw;
95
+ let to = toRaw;
96
+ let flow = opInfo.energyFlow;
97
+ if (to === centerId && from !== centerId) {
98
+ [from, to] = [to, from];
99
+ if (flow === "from") flow = "to";
100
+ else if (flow === "to") flow = "from";
101
+ }
102
+ relationships.push({
103
+ type: opInfo.relType,
104
+ from,
105
+ to,
106
+ label,
107
+ energyFlow: flow
108
+ });
109
+ i++;
110
+ continue;
111
+ }
112
+ const sysMatch = trimmed.match(
113
+ /^([a-zA-Z][a-zA-Z0-9_-]*)\s*(\[.*\])?\s*$/
114
+ );
115
+ if (sysMatch) {
116
+ const id = sysMatch[1].toLowerCase();
117
+ if (!knownIds.has(id)) {
118
+ const props = sysMatch[2] ? parseProps(sysMatch[2]) : {};
119
+ individuals.push(buildIndividual(id, props, false));
120
+ knownIds.add(id);
121
+ }
122
+ i++;
123
+ continue;
124
+ }
125
+ throw new EcomapParseError(`Unexpected: ${trimmed}`, i + 1);
126
+ }
127
+ return { type: "ecomap", individuals, relationships, metadata };
128
+ }
129
+ function leadingSpaces(line) {
130
+ const m = line.match(/^(\s*)/);
131
+ return m ? m[1].length : 0;
132
+ }
133
+ function parseProps(bracketStr) {
134
+ const inner = bracketStr.slice(1, -1).trim();
135
+ if (!inner) return {};
136
+ const props = {};
137
+ for (const part of splitCommas(inner)) {
138
+ const t = part.trim();
139
+ if (!t) continue;
140
+ const kv = t.match(/^([\w][\w-]*)\s*:\s*(?:"([^"]*)"|(\S+))$/);
141
+ if (kv) {
142
+ props[kv[1]] = kv[2] ?? kv[3];
143
+ continue;
144
+ }
145
+ if (t === "male" || t === "female" || t === "unknown") {
146
+ props.sex = t;
147
+ continue;
148
+ }
149
+ props[t] = "true";
150
+ }
151
+ return props;
152
+ }
153
+ function splitCommas(s) {
154
+ const parts = [];
155
+ let cur = "";
156
+ let inQ = false;
157
+ for (const ch of s) {
158
+ if (ch === '"') {
159
+ inQ = !inQ;
160
+ cur += ch;
161
+ } else if (ch === "," && !inQ) {
162
+ parts.push(cur);
163
+ cur = "";
164
+ } else {
165
+ cur += ch;
166
+ }
167
+ }
168
+ if (cur) parts.push(cur);
169
+ return parts;
170
+ }
171
+ function parseSex(s) {
172
+ switch (s) {
173
+ case "male":
174
+ case "female":
175
+ case "unknown":
176
+ case "other":
177
+ case "nonbinary":
178
+ case "intersex":
179
+ return s;
180
+ default:
181
+ return "unknown";
182
+ }
183
+ }
184
+ function buildIndividual(id, props, isCenter) {
185
+ const properties = {};
186
+ if (isCenter) properties.center = "true";
187
+ if (props.category) properties.category = props.category;
188
+ if (props.size) properties.size = props.size;
189
+ if (props.importance) properties.importance = props.importance;
190
+ if (props.sector) properties.sector = props.sector;
191
+ return {
192
+ id,
193
+ label: props.label ?? id,
194
+ sex: parseSex(props.sex),
195
+ status: "alive",
196
+ age: props.age ? parseInt(props.age, 10) : void 0,
197
+ properties
198
+ };
199
+ }
200
+
201
+ // src/diagrams/ecomap/layout.ts
202
+ var CENTER_R = 50;
203
+ var SYS_R = 30;
204
+ var SYS_R_LARGE = 40;
205
+ var SYS_R_SMALL = 20;
206
+ var PADDING = 40;
207
+ var LABEL_CLEARANCE = 60;
208
+ function layoutEcomap(ast, _config) {
209
+ const center = ast.individuals.find(
210
+ (ind) => ind.properties?.center === "true"
211
+ );
212
+ if (!center) throw new Error("Ecomap requires a center element");
213
+ const systems = ast.individuals.filter(
214
+ (ind) => ind.properties?.center !== "true"
215
+ );
216
+ const n = systems.length;
217
+ if (n === 0) {
218
+ const size = (CENTER_R + PADDING + LABEL_CLEARANCE) * 2;
219
+ return {
220
+ width: size,
221
+ height: size,
222
+ nodes: [
223
+ {
224
+ id: center.id,
225
+ x: size / 2 - CENTER_R,
226
+ y: size / 2 - CENTER_R,
227
+ width: CENTER_R * 2,
228
+ height: CENTER_R * 2,
229
+ generation: 0,
230
+ individual: center
231
+ }
232
+ ],
233
+ edges: []
234
+ };
235
+ }
236
+ const ringRadii = getRingRadii(n);
237
+ const ringAssignment = assignToRings(
238
+ center.id,
239
+ systems,
240
+ ast.relationships,
241
+ ringRadii.length
242
+ );
243
+ const systemPositions = placeOnRings(systems, ringAssignment, ringRadii);
244
+ const maxRing = ringRadii[ringRadii.length - 1] ?? 180;
245
+ const canvasSize = (maxRing + SYS_R_LARGE + LABEL_CLEARANCE + PADDING) * 2;
246
+ const cx = canvasSize / 2;
247
+ const cy = canvasSize / 2;
248
+ const nodes = [];
249
+ nodes.push({
250
+ id: center.id,
251
+ x: cx - CENTER_R,
252
+ y: cy - CENTER_R,
253
+ width: CENTER_R * 2,
254
+ height: CENTER_R * 2,
255
+ generation: 0,
256
+ individual: center
257
+ });
258
+ for (const sys of systems) {
259
+ const pos = systemPositions.get(sys.id);
260
+ if (!pos) continue;
261
+ const r = getSystemRadius(sys);
262
+ nodes.push({
263
+ id: sys.id,
264
+ x: cx + pos.x - r,
265
+ y: cy + pos.y - r,
266
+ width: r * 2,
267
+ height: r * 2,
268
+ generation: pos.ring + 1,
269
+ individual: sys
270
+ });
271
+ }
272
+ const edges = computeEdges(nodes, ast.relationships);
273
+ return { width: canvasSize, height: canvasSize, nodes, edges };
274
+ }
275
+ function getRingRadii(n) {
276
+ if (n === 0) return [];
277
+ if (n <= 6) return [180];
278
+ if (n <= 12) return [150, 270];
279
+ return [140, 230, 320];
280
+ }
281
+ function assignToRings(_centerId, systems, rels, ringCount) {
282
+ const relBySys = /* @__PURE__ */ new Map();
283
+ for (const r of rels) {
284
+ const sysId = r.from === _centerId ? r.to : r.from === _centerId ? r.to : r.to;
285
+ if (!relBySys.has(sysId)) relBySys.set(sysId, r);
286
+ const otherId = r.from === _centerId ? r.to : r.from;
287
+ if (!relBySys.has(otherId)) relBySys.set(otherId, r);
288
+ }
289
+ const result = /* @__PURE__ */ new Map();
290
+ if (ringCount <= 1) {
291
+ for (const s of systems) result.set(s.id, 0);
292
+ return result;
293
+ }
294
+ for (const s of systems) {
295
+ const rel = relBySys.get(s.id);
296
+ if (!rel) {
297
+ result.set(s.id, ringCount - 1);
298
+ continue;
299
+ }
300
+ if (rel.type === "strong" || rel.type === "stressful-strong") {
301
+ result.set(s.id, 0);
302
+ } else if (rel.type === "weak" || rel.type === "broken") {
303
+ result.set(s.id, ringCount - 1);
304
+ } else {
305
+ result.set(s.id, Math.min(1, ringCount - 1));
306
+ }
307
+ }
308
+ return result;
309
+ }
310
+ function placeOnRings(systems, ringAssignment, ringRadii) {
311
+ const ringGroups = /* @__PURE__ */ new Map();
312
+ for (const s of systems) {
313
+ const ring = ringAssignment.get(s.id) ?? 0;
314
+ const grp = ringGroups.get(ring) ?? [];
315
+ grp.push(s);
316
+ ringGroups.set(ring, grp);
317
+ }
318
+ const positions = /* @__PURE__ */ new Map();
319
+ for (const [ring, grp] of ringGroups) {
320
+ const radius = ringRadii[ring] ?? ringRadii[ringRadii.length - 1] ?? 180;
321
+ const n = grp.length;
322
+ const startAngle = -Math.PI / 2;
323
+ for (let idx = 0; idx < n; idx++) {
324
+ const angle = startAngle + 2 * Math.PI * idx / n;
325
+ positions.set(grp[idx].id, {
326
+ x: radius * Math.cos(angle),
327
+ y: radius * Math.sin(angle),
328
+ ring
329
+ });
330
+ }
331
+ }
332
+ return positions;
333
+ }
334
+ function computeEdges(nodes, rels) {
335
+ const nodeMap = /* @__PURE__ */ new Map();
336
+ for (const n of nodes) nodeMap.set(n.id, n);
337
+ const edges = [];
338
+ for (const rel of rels) {
339
+ const fromNode = nodeMap.get(rel.from);
340
+ const toNode = nodeMap.get(rel.to);
341
+ if (!fromNode || !toNode) continue;
342
+ const fcx = fromNode.x + fromNode.width / 2;
343
+ const fcy = fromNode.y + fromNode.height / 2;
344
+ const tcx = toNode.x + toNode.width / 2;
345
+ const tcy = toNode.y + toNode.height / 2;
346
+ const dx = tcx - fcx;
347
+ const dy = tcy - fcy;
348
+ const dist = Math.sqrt(dx * dx + dy * dy);
349
+ if (dist < 1) continue;
350
+ const ux = dx / dist;
351
+ const uy = dy / dist;
352
+ const fromR = fromNode.width / 2;
353
+ const toR = toNode.width / 2;
354
+ const x1 = fcx + ux * fromR;
355
+ const y1 = fcy + uy * fromR;
356
+ const x2 = tcx - ux * toR;
357
+ const y2 = tcy - uy * toR;
358
+ edges.push({
359
+ from: rel.from,
360
+ to: rel.to,
361
+ relationship: rel,
362
+ path: `M ${x1} ${y1} L ${x2} ${y2}`
363
+ });
364
+ }
365
+ return edges;
366
+ }
367
+ function getSystemRadius(ind) {
368
+ const imp = ind.properties?.importance ?? ind.properties?.size;
369
+ switch (imp) {
370
+ case "major":
371
+ case "large":
372
+ return SYS_R_LARGE;
373
+ case "minor":
374
+ case "small":
375
+ return SYS_R_SMALL;
376
+ default:
377
+ return SYS_R;
378
+ }
379
+ }
380
+
381
+ // src/diagrams/ecomap/renderer.ts
382
+ var CATEGORY_COLORS = {
383
+ family: "#8D6E63",
384
+ friends: "#42A5F5",
385
+ work: "#66BB6A",
386
+ education: "#FFA726",
387
+ health: "#EF5350",
388
+ "mental-health": "#AB47BC",
389
+ religion: "#CCBB33",
390
+ recreation: "#26C6DA",
391
+ legal: "#78909C",
392
+ government: "#8D6E63",
393
+ financial: "#66BB6A",
394
+ community: "#29B6F6",
395
+ cultural: "#FFA726",
396
+ substance: "#EF5350",
397
+ technology: "#42A5F5",
398
+ pet: "#8D6E63"
399
+ };
400
+ function renderEcomap(layout, config) {
401
+ const centerNode = layout.nodes.find(
402
+ (n) => n.individual.properties?.center === "true"
403
+ );
404
+ const systemNodes = layout.nodes.filter(
405
+ (n) => n.individual.properties?.center !== "true"
406
+ );
407
+ const t = resolveBaseTheme(config.theme);
408
+ const defsStr = buildDefs(t);
409
+ const styleStr = buildStyles(config, t);
410
+ const connectionsStr = renderConnections(layout.edges);
411
+ const centerStr = centerNode ? renderCenter(centerNode, config) : "";
412
+ const systemsStr = renderSystems(systemNodes, config);
413
+ const labelsStr = renderConnectionLabels(layout.edges, t);
414
+ return svgRoot(
415
+ {
416
+ viewBox: `0 0 ${layout.width} ${layout.height}`,
417
+ class: "schematex-diagram schematex-ecomap",
418
+ width: layout.width,
419
+ height: layout.height
420
+ },
421
+ [
422
+ title("Ecomap"),
423
+ desc(
424
+ `Ecomap diagram with ${systemNodes.length} external systems`
425
+ ),
426
+ defsStr,
427
+ styleStr,
428
+ connectionsStr,
429
+ centerStr,
430
+ systemsStr,
431
+ labelsStr
432
+ ]
433
+ );
434
+ }
435
+ function buildDefs(t) {
436
+ const arrowMarker = el(
437
+ "marker",
438
+ {
439
+ id: "schematex-ecomap-eco-arrow",
440
+ viewBox: "0 0 10 10",
441
+ refX: "10",
442
+ refY: "5",
443
+ markerWidth: "8",
444
+ markerHeight: "8",
445
+ orient: "auto-start-reverse"
446
+ },
447
+ [el("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: t.neutral })]
448
+ );
449
+ return el("defs", {}, [arrowMarker]);
450
+ }
451
+ function buildStyles(config, t) {
452
+ let css = `
453
+ .schematex-ecomap {${cssCustomProperties(t)}
454
+ background: ${t.bg};
455
+ }
456
+ .schematex-ecomap-center-shape { fill: ${t.fill}; stroke: ${t.stroke}; stroke-width: ${STROKE_WIDTH.thick}; }
457
+ .schematex-ecomap-center-label { font-family: ${config.fontFamily}; font-size: ${config.fontSize + 2}px; text-anchor: middle; dominant-baseline: central; fill: ${t.text}; font-weight: 600; }
458
+ .schematex-ecomap-system-shape { fill: ${t.fillMuted}; stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
459
+ .schematex-ecomap-system-label { font-family: ${config.fontFamily}; font-size: ${config.fontSize - 1}px; text-anchor: middle; fill: ${t.text}; }
460
+ .schematex-ecomap-eco-line { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; fill: none; stroke-linecap: round; }
461
+ .schematex-ecomap-eco-line-parallel { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; fill: none; stroke-linecap: round; }
462
+ .schematex-ecomap-eco-line-weak { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 6,4; fill: none; stroke-linecap: round; }
463
+ .schematex-ecomap-eco-line-broken { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 3,8; fill: none; stroke-linecap: round; }
464
+ .schematex-ecomap-eco-line-stressful { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; fill: none; stroke-linecap: round; }
465
+ .schematex-ecomap-eco-conn-label { font-family: ${config.fontFamily}; font-size: ${config.fontSize - 2}px; text-anchor: middle; fill: ${t.textMuted}; }
466
+ .schematex-ecomap-eco-arrow { fill: ${t.neutral}; }
467
+ `;
468
+ for (const [cat, color] of Object.entries(CATEGORY_COLORS)) {
469
+ css += `.schematex-ecomap-system-${cat} .schematex-ecomap-system-shape { fill: ${color}18; stroke: ${color}; }
470
+ `;
471
+ }
472
+ return el("style", {}, css);
473
+ }
474
+ function renderCenter(node, config) {
475
+ const cx = node.x + node.width / 2;
476
+ const cy = node.y + node.height / 2;
477
+ const r = node.width / 2;
478
+ const ind = node.individual;
479
+ const label = ind.label !== ind.id ? ind.label : capitalize(ind.id);
480
+ const elements = [
481
+ el("circle", {
482
+ cx,
483
+ cy,
484
+ r,
485
+ class: "schematex-ecomap-center-shape"
486
+ }),
487
+ text(
488
+ {
489
+ x: cx,
490
+ y: cy,
491
+ class: "schematex-ecomap-center-label"
492
+ },
493
+ label
494
+ )
495
+ ];
496
+ if (ind.age) {
497
+ elements.push(
498
+ text(
499
+ {
500
+ x: cx,
501
+ y: cy + config.fontSize + 4,
502
+ class: "schematex-ecomap-center-label",
503
+ "font-size": `${config.fontSize}px`,
504
+ "font-weight": "normal"
505
+ },
506
+ `Age ${ind.age}`
507
+ )
508
+ );
509
+ }
510
+ return group(
511
+ { class: "schematex-ecomap-center", "data-id": ind.id },
512
+ elements
513
+ );
514
+ }
515
+ function renderSystems(nodes, config) {
516
+ const elements = [];
517
+ for (const node of nodes) {
518
+ const cx = node.x + node.width / 2;
519
+ const cy = node.y + node.height / 2;
520
+ const r = node.width / 2;
521
+ const ind = node.individual;
522
+ const cat = ind.properties?.category ?? "other";
523
+ const label = ind.label !== ind.id ? ind.label : capitalize(ind.id);
524
+ const labelLines = wrapLabel(label, 12);
525
+ const labelElements = [];
526
+ const labelStartY = cy + r + config.fontSize + 2;
527
+ for (let li = 0; li < labelLines.length; li++) {
528
+ labelElements.push(
529
+ text(
530
+ {
531
+ x: cx,
532
+ y: labelStartY + li * (config.fontSize + 1),
533
+ class: "schematex-ecomap-system-label"
534
+ },
535
+ labelLines[li]
536
+ )
537
+ );
538
+ }
539
+ elements.push(
540
+ group(
541
+ {
542
+ class: `schematex-ecomap-system schematex-ecomap-system-${cat}`,
543
+ "data-system-id": ind.id
544
+ },
545
+ [
546
+ el("circle", {
547
+ cx,
548
+ cy,
549
+ r,
550
+ class: "schematex-ecomap-system-shape"
551
+ }),
552
+ ...labelElements
553
+ ]
554
+ )
555
+ );
556
+ }
557
+ return group({ class: "schematex-ecomap-systems" }, elements);
558
+ }
559
+ function renderConnections(edges) {
560
+ const elements = [];
561
+ for (const edge of edges) {
562
+ const coords = extractLineCoords(edge.path);
563
+ if (!coords) continue;
564
+ const relType = edge.relationship.type;
565
+ const flow = edge.relationship.energyFlow;
566
+ const lineElements = [];
567
+ const arrowAttrs = getArrowAttrs(flow);
568
+ switch (relType) {
569
+ case "strong":
570
+ lineElements.push(
571
+ ...parallelLines(coords, 3, 4, {
572
+ class: "schematex-ecomap-eco-line-parallel",
573
+ ...arrowAttrs
574
+ })
575
+ );
576
+ break;
577
+ case "moderate":
578
+ lineElements.push(
579
+ ...parallelLines(coords, 2, 4, {
580
+ class: "schematex-ecomap-eco-line-parallel",
581
+ ...arrowAttrs
582
+ })
583
+ );
584
+ break;
585
+ case "weak":
586
+ lineElements.push(
587
+ el("line", {
588
+ x1: coords.x1,
589
+ y1: coords.y1,
590
+ x2: coords.x2,
591
+ y2: coords.y2,
592
+ class: "schematex-ecomap-eco-line-weak",
593
+ ...arrowAttrs
594
+ })
595
+ );
596
+ break;
597
+ case "stressful":
598
+ case "stressful-strong":
599
+ lineElements.push(
600
+ el("path", {
601
+ d: wavyPath(
602
+ coords.x1,
603
+ coords.y1,
604
+ coords.x2,
605
+ coords.y2,
606
+ 8,
607
+ 20
608
+ ),
609
+ class: "schematex-ecomap-eco-line-stressful",
610
+ "stroke-width": relType === "stressful-strong" ? "3" : "2",
611
+ ...arrowAttrs
612
+ })
613
+ );
614
+ break;
615
+ case "conflictual":
616
+ lineElements.push(
617
+ el("path", {
618
+ d: wavyPath(
619
+ coords.x1,
620
+ coords.y1,
621
+ coords.x2,
622
+ coords.y2,
623
+ 8,
624
+ 20
625
+ ),
626
+ class: "schematex-ecomap-eco-line-stressful",
627
+ ...arrowAttrs
628
+ })
629
+ );
630
+ lineElements.push(
631
+ ...conflictMarks(coords)
632
+ );
633
+ break;
634
+ case "broken":
635
+ lineElements.push(
636
+ el("line", {
637
+ x1: coords.x1,
638
+ y1: coords.y1,
639
+ x2: coords.x2,
640
+ y2: coords.y2,
641
+ class: "schematex-ecomap-eco-line-broken",
642
+ ...arrowAttrs
643
+ })
644
+ );
645
+ break;
646
+ default:
647
+ lineElements.push(
648
+ el("line", {
649
+ x1: coords.x1,
650
+ y1: coords.y1,
651
+ x2: coords.x2,
652
+ y2: coords.y2,
653
+ class: "schematex-ecomap-eco-line",
654
+ ...arrowAttrs
655
+ })
656
+ );
657
+ break;
658
+ }
659
+ elements.push(
660
+ group(
661
+ {
662
+ class: `schematex-ecomap-connection schematex-ecomap-connection-${relType}`,
663
+ "data-from": edge.from,
664
+ "data-to": edge.to
665
+ },
666
+ lineElements
667
+ )
668
+ );
669
+ }
670
+ return group({ class: "schematex-ecomap-connections" }, elements);
671
+ }
672
+ function renderConnectionLabels(edges, t) {
673
+ const elements = [];
674
+ for (const edge of edges) {
675
+ if (!edge.relationship.label) continue;
676
+ const coords = extractLineCoords(edge.path);
677
+ if (!coords) continue;
678
+ const mx = (coords.x1 + coords.x2) / 2;
679
+ const my = (coords.y1 + coords.y2) / 2;
680
+ elements.push(
681
+ group({ class: "schematex-ecomap-conn-label-group" }, [
682
+ el("rect", {
683
+ x: mx - 40,
684
+ y: my - 8,
685
+ width: 80,
686
+ height: 16,
687
+ rx: 3,
688
+ fill: t.bg,
689
+ "fill-opacity": "0.85"
690
+ }),
691
+ text(
692
+ { x: mx, y: my + 4, class: "schematex-ecomap-eco-conn-label" },
693
+ edge.relationship.label
694
+ )
695
+ ])
696
+ );
697
+ }
698
+ return group({ class: "schematex-ecomap-connection-labels" }, elements);
699
+ }
700
+ function extractLineCoords(pathData) {
701
+ const m = pathData.match(
702
+ /M\s*([\d.-]+)\s+([\d.-]+)\s*L\s*([\d.-]+)\s+([\d.-]+)/
703
+ );
704
+ if (!m) return null;
705
+ return { x1: +m[1], y1: +m[2], x2: +m[3], y2: +m[4] };
706
+ }
707
+ function parallelLines(coords, count, gap, attrs) {
708
+ const { x1, y1, x2, y2 } = coords;
709
+ const dx = x2 - x1;
710
+ const dy = y2 - y1;
711
+ const len = Math.sqrt(dx * dx + dy * dy);
712
+ if (len < 1) return [];
713
+ const nx = -dy / len;
714
+ const ny = dx / len;
715
+ const lines = [];
716
+ const offsets = count === 2 ? [-gap / 2, gap / 2] : count === 3 ? [-gap, 0, gap] : [0];
717
+ for (const off of offsets) {
718
+ lines.push(
719
+ el("line", {
720
+ x1: x1 + nx * off,
721
+ y1: y1 + ny * off,
722
+ x2: x2 + nx * off,
723
+ y2: y2 + ny * off,
724
+ ...attrs
725
+ })
726
+ );
727
+ }
728
+ return lines;
729
+ }
730
+ function wavyPath(x1, y1, x2, y2, amplitude, wavelength) {
731
+ const dx = x2 - x1;
732
+ const dy = y2 - y1;
733
+ const len = Math.sqrt(dx * dx + dy * dy);
734
+ const segments = Math.max(4, Math.round(len / wavelength));
735
+ const ux = dx / len;
736
+ const uy = dy / len;
737
+ const nx = -uy;
738
+ const ny = ux;
739
+ let d = `M ${x1} ${y1}`;
740
+ for (let i = 0; i < segments; i++) {
741
+ const t1 = (i + 0.5) / segments;
742
+ const t2 = (i + 1) / segments;
743
+ const sign = i % 2 === 0 ? 1 : -1;
744
+ const cpx = x1 + dx * t1 + nx * amplitude * sign;
745
+ const cpy = y1 + dy * t1 + ny * amplitude * sign;
746
+ const ex = x1 + dx * t2;
747
+ const ey = y1 + dy * t2;
748
+ d += ` Q ${cpx} ${cpy} ${ex} ${ey}`;
749
+ }
750
+ return d;
751
+ }
752
+ function conflictMarks(coords) {
753
+ const mx = (coords.x1 + coords.x2) / 2;
754
+ const my = (coords.y1 + coords.y2) / 2;
755
+ const dx = coords.x2 - coords.x1;
756
+ const dy = coords.y2 - coords.y1;
757
+ const len = Math.sqrt(dx * dx + dy * dy);
758
+ if (len < 1) return [];
759
+ const nx = -dy / len;
760
+ const ny = dx / len;
761
+ const markLen = 6;
762
+ return [
763
+ el("line", {
764
+ x1: mx - 3 + nx * markLen,
765
+ y1: my - 3 + ny * markLen,
766
+ x2: mx - 3 - nx * markLen,
767
+ y2: my - 3 - ny * markLen,
768
+ class: "schematex-ecomap-eco-line",
769
+ "stroke-width": "2"
770
+ }),
771
+ el("line", {
772
+ x1: mx + 3 + nx * markLen,
773
+ y1: my + 3 + ny * markLen,
774
+ x2: mx + 3 - nx * markLen,
775
+ y2: my + 3 - ny * markLen,
776
+ class: "schematex-ecomap-eco-line",
777
+ "stroke-width": "2"
778
+ })
779
+ ];
780
+ }
781
+ function getArrowAttrs(flow) {
782
+ if (!flow || flow === "none") return {};
783
+ switch (flow) {
784
+ case "from":
785
+ return { "marker-end": "url(#schematex-ecomap-eco-arrow)" };
786
+ case "to":
787
+ return { "marker-start": "url(#schematex-ecomap-eco-arrow)" };
788
+ case "mutual":
789
+ return {
790
+ "marker-start": "url(#schematex-ecomap-eco-arrow)",
791
+ "marker-end": "url(#schematex-ecomap-eco-arrow)"
792
+ };
793
+ default:
794
+ return {};
795
+ }
796
+ }
797
+ function capitalize(s) {
798
+ return s.charAt(0).toUpperCase() + s.slice(1);
799
+ }
800
+ function wrapLabel(label, maxChars) {
801
+ if (label.length <= maxChars) return [label];
802
+ const words = label.split(/\s+/);
803
+ const lines = [];
804
+ let current = "";
805
+ for (const word of words) {
806
+ if (current && current.length + word.length + 1 > maxChars) {
807
+ lines.push(current);
808
+ current = word;
809
+ } else {
810
+ current = current ? current + " " + word : word;
811
+ }
812
+ }
813
+ if (current) lines.push(current);
814
+ return lines;
815
+ }
816
+
817
+ // src/diagrams/ecomap/index.ts
818
+ var ecomap = {
819
+ type: "ecomap",
820
+ detect(text2) {
821
+ const firstLine = text2.trim().split("\n")[0]?.trim().toLowerCase() ?? "";
822
+ return firstLine === "ecomap" || firstLine.startsWith("ecomap ");
823
+ },
824
+ render(text2, config) {
825
+ const ast = parseEcomap(text2);
826
+ const layout = layoutEcomap(ast);
827
+ const renderConfig = {
828
+ fontFamily: config?.fontFamily ?? "system-ui, -apple-system, sans-serif",
829
+ fontSize: config?.fontSize ?? 12,
830
+ theme: config?.theme ?? "default",
831
+ padding: config?.padding ?? 20
832
+ };
833
+ return renderEcomap(layout, renderConfig);
834
+ }
835
+ };
836
+
837
+ export { EcomapParseError, ecomap, layoutEcomap, parseEcomap, renderEcomap };
838
+ //# sourceMappingURL=chunk-U5GGE6PJ.js.map
839
+ //# sourceMappingURL=chunk-U5GGE6PJ.js.map