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,770 @@
1
+ import { resolveBaseTheme, STROKE_WIDTH, FONT_SIZE, cssCustomProperties } from './chunk-IX554O5K.js';
2
+ import { text, title, desc, el, group, svgRoot, line, circle, polygon } from './chunk-KLJEK547.js';
3
+
4
+ // src/diagrams/sociogram/parser.ts
5
+ var SociogramParseError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "SociogramParseError";
9
+ }
10
+ };
11
+ var EDGE_OPS = [
12
+ ["<===>", { direction: "mutual", valence: "positive", weight: 4 }],
13
+ ["===>", { direction: "one-way", valence: "positive", weight: 4 }],
14
+ ["<===", { direction: "one-way", valence: "positive", weight: 4 }],
15
+ ["<==>", { direction: "mutual", valence: "positive", weight: 3 }],
16
+ ["==>", { direction: "one-way", valence: "positive", weight: 3 }],
17
+ ["<==", { direction: "one-way", valence: "positive", weight: 3 }],
18
+ ["===", { direction: "undirected", valence: "positive", weight: 3 }],
19
+ ["<x->", { direction: "mutual", valence: "negative", weight: 2 }],
20
+ ["-x>", { direction: "one-way", valence: "negative", weight: 2 }],
21
+ ["<x-", { direction: "one-way", valence: "negative", weight: 2 }],
22
+ ["-x-", { direction: "undirected", valence: "negative", weight: 2 }],
23
+ ["<.->", { direction: "mutual", valence: "neutral", weight: 2 }],
24
+ [".->", { direction: "one-way", valence: "neutral", weight: 2 }],
25
+ // part of -.>
26
+ ["-.-", { direction: "undirected", valence: "neutral", weight: 2 }],
27
+ ["<->", { direction: "mutual", valence: "positive", weight: 2 }],
28
+ ["->", { direction: "one-way", valence: "positive", weight: 2 }],
29
+ ["<-", { direction: "one-way", valence: "positive", weight: 2 }],
30
+ ["--", { direction: "undirected", valence: "positive", weight: 2 }]
31
+ ];
32
+ function findEdgeOp(line2) {
33
+ for (const [opStr, op] of EDGE_OPS) {
34
+ const idx = line2.indexOf(` ${opStr} `);
35
+ if (idx === -1) continue;
36
+ const leftId = line2.slice(0, idx).trim();
37
+ const afterOp = line2.slice(idx + opStr.length + 2).trim();
38
+ if (!leftId || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(leftId)) continue;
39
+ const rightMatch = afterOp.match(/^([a-zA-Z][a-zA-Z0-9_-]*)(.*)/);
40
+ if (!rightMatch) continue;
41
+ const rightId = rightMatch[1];
42
+ const rest = rightMatch[2].trim();
43
+ if (opStr === "<-" || opStr === "<==" || opStr === "<===" || opStr === "<x-") {
44
+ return { leftId: rightId, rightId: leftId, op: { ...op, direction: "one-way" }, rest };
45
+ }
46
+ return { leftId, rightId, op, rest };
47
+ }
48
+ const dashDotMatch = line2.match(/^([a-zA-Z][a-zA-Z0-9_-]*)\s+-\.>\s+([a-zA-Z][a-zA-Z0-9_-]*)(.*)/);
49
+ if (dashDotMatch) {
50
+ return {
51
+ leftId: dashDotMatch[1],
52
+ rightId: dashDotMatch[2],
53
+ op: { direction: "one-way", valence: "neutral", weight: 2 },
54
+ rest: dashDotMatch[3].trim()
55
+ };
56
+ }
57
+ return null;
58
+ }
59
+ function parseProperties(propsStr) {
60
+ const result = {};
61
+ const regex = /(\w[\w-]*):\s*(?:"([^"]*)"|([^\s,\]]+))/g;
62
+ let match;
63
+ while ((match = regex.exec(propsStr)) !== null) {
64
+ result[match[1]] = match[2] ?? match[3];
65
+ }
66
+ return result;
67
+ }
68
+ function extractProps(line2) {
69
+ const bracketMatch = line2.match(/\[([^\]]*)\]/);
70
+ if (!bracketMatch) return { clean: line2, props: {} };
71
+ const clean = line2.slice(0, bracketMatch.index).trim();
72
+ const props = parseProperties(bracketMatch[1]);
73
+ return { clean, props };
74
+ }
75
+ function parseSociogram(text2) {
76
+ const lines = text2.split("\n");
77
+ let lineIdx = 0;
78
+ while (lineIdx < lines.length && !lines[lineIdx].trim()) lineIdx++;
79
+ const headerLine = lines[lineIdx]?.trim() ?? "";
80
+ if (!headerLine.toLowerCase().startsWith("sociogram")) {
81
+ throw new SociogramParseError("Sociogram must start with 'sociogram'");
82
+ }
83
+ lineIdx++;
84
+ let titleStr;
85
+ const titleMatch = headerLine.match(/"([^"]+)"/);
86
+ if (titleMatch) titleStr = titleMatch[1];
87
+ const config = {
88
+ layout: "circular",
89
+ sizing: "uniform",
90
+ coloring: "default",
91
+ highlight: ["stars", "isolates"]
92
+ };
93
+ const nodes = [];
94
+ const edges = [];
95
+ const groups = [];
96
+ const nodeIds = /* @__PURE__ */ new Set();
97
+ let currentGroup = null;
98
+ while (lineIdx < lines.length) {
99
+ const raw = lines[lineIdx];
100
+ const trimmed = raw.trim();
101
+ lineIdx++;
102
+ if (!trimmed || trimmed.startsWith("#")) continue;
103
+ if (trimmed.startsWith("config:")) {
104
+ const configBody = trimmed.slice(7).trim();
105
+ const eqIdx = configBody.indexOf("=");
106
+ if (eqIdx !== -1) {
107
+ const key = configBody.slice(0, eqIdx).trim();
108
+ const val = configBody.slice(eqIdx + 1).trim();
109
+ switch (key) {
110
+ case "layout":
111
+ if (["circular", "force-directed", "concentric"].includes(val)) {
112
+ config.layout = val;
113
+ }
114
+ break;
115
+ case "sizing":
116
+ if (["uniform", "in-degree", "betweenness"].includes(val)) {
117
+ config.sizing = val;
118
+ }
119
+ break;
120
+ case "coloring":
121
+ if (["default", "group", "role"].includes(val)) {
122
+ config.coloring = val;
123
+ }
124
+ break;
125
+ case "highlight":
126
+ config.highlight = val.split(",").map((s) => s.trim());
127
+ break;
128
+ }
129
+ }
130
+ continue;
131
+ }
132
+ if (trimmed.startsWith("group ")) {
133
+ const { clean: clean2, props: props2 } = extractProps(trimmed.slice(6).trim());
134
+ const groupId = clean2.split(/\s/)[0];
135
+ currentGroup = {
136
+ id: groupId,
137
+ label: props2.label ?? groupId,
138
+ color: props2.color,
139
+ members: []
140
+ };
141
+ groups.push(currentGroup);
142
+ continue;
143
+ }
144
+ const indent = raw.search(/\S/);
145
+ if (currentGroup && indent >= 4) {
146
+ const { clean: clean2, props: props2 } = extractProps(trimmed);
147
+ const memberId = clean2.split(/\s/)[0];
148
+ if (memberId && /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(memberId)) {
149
+ currentGroup.members.push(memberId);
150
+ if (!nodeIds.has(memberId)) {
151
+ nodeIds.add(memberId);
152
+ nodes.push({
153
+ id: memberId,
154
+ label: props2.label,
155
+ group: currentGroup.id,
156
+ role: props2.role,
157
+ size: props2.size
158
+ });
159
+ }
160
+ continue;
161
+ }
162
+ }
163
+ if (indent < 4) {
164
+ currentGroup = null;
165
+ }
166
+ const edgeResult = findEdgeOp(trimmed);
167
+ if (edgeResult) {
168
+ const { leftId, rightId, op, rest } = edgeResult;
169
+ const edgeProps = extractProps(rest).props;
170
+ for (const id of [leftId, rightId]) {
171
+ if (!nodeIds.has(id)) {
172
+ nodeIds.add(id);
173
+ nodes.push({ id });
174
+ }
175
+ }
176
+ const weight = edgeProps.weight ? Number(edgeProps.weight) : op.weight;
177
+ edges.push({
178
+ from: leftId,
179
+ to: rightId,
180
+ direction: op.direction,
181
+ valence: op.valence,
182
+ weight,
183
+ label: edgeProps.label
184
+ });
185
+ continue;
186
+ }
187
+ const { clean, props } = extractProps(trimmed);
188
+ const nodeId = clean.split(/\s/)[0];
189
+ if (nodeId && /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(nodeId) && !nodeIds.has(nodeId)) {
190
+ nodeIds.add(nodeId);
191
+ const node = {
192
+ id: nodeId,
193
+ label: props.label,
194
+ group: props.group,
195
+ role: props.role,
196
+ size: props.size
197
+ };
198
+ nodes.push(node);
199
+ if (props.group) {
200
+ let grp = groups.find((g) => g.id === props.group);
201
+ if (!grp) {
202
+ grp = { id: props.group, members: [] };
203
+ groups.push(grp);
204
+ }
205
+ grp.members.push(nodeId);
206
+ }
207
+ }
208
+ }
209
+ return {
210
+ type: "sociogram",
211
+ title: titleStr,
212
+ config,
213
+ nodes,
214
+ edges,
215
+ groups
216
+ };
217
+ }
218
+
219
+ // src/diagrams/sociogram/layout.ts
220
+ function computeInDegree(nodeId, edges) {
221
+ let count = 0;
222
+ for (const e of edges) {
223
+ if (e.to === nodeId) count++;
224
+ if (e.direction === "mutual" && e.from === nodeId) count++;
225
+ if (e.direction === "undirected" && e.from === nodeId) count++;
226
+ }
227
+ return count;
228
+ }
229
+ function computeOutDegree(nodeId, edges) {
230
+ let count = 0;
231
+ for (const e of edges) {
232
+ if (e.from === nodeId) count++;
233
+ if (e.direction === "mutual" && e.to === nodeId) count++;
234
+ if (e.direction === "undirected" && e.to === nodeId) count++;
235
+ }
236
+ return count;
237
+ }
238
+ function autoDetectRole(node, inDeg, outDeg, meanIn, sdIn, rejectionCount) {
239
+ if (node.role) return node.role;
240
+ if (inDeg === 0 && outDeg === 0) return "isolate";
241
+ if (inDeg === 0 && outDeg > 0) return "neglectee";
242
+ if (rejectionCount >= 2) return "rejected";
243
+ if (sdIn > 0 && inDeg >= meanIn + 1.5 * sdIn) return "star";
244
+ return void 0;
245
+ }
246
+ var BASE_RADIUS = 20;
247
+ function computeNodeRadius(node, inDeg, sizing) {
248
+ if (node.size === "small") return 14;
249
+ if (node.size === "large") return 30;
250
+ if (sizing === "in-degree") {
251
+ return Math.min(40, Math.max(14, 14 + inDeg * 4));
252
+ }
253
+ return BASE_RADIUS;
254
+ }
255
+ function layoutCircular(ast) {
256
+ const n = ast.nodes.length;
257
+ const radius = Math.max(120, n * 22);
258
+ const canvasSize = radius * 2 + 160;
259
+ const cx = canvasSize / 2;
260
+ const cy = canvasSize / 2;
261
+ const inDegrees = /* @__PURE__ */ new Map();
262
+ const outDegrees = /* @__PURE__ */ new Map();
263
+ const rejections = /* @__PURE__ */ new Map();
264
+ for (const node of ast.nodes) {
265
+ inDegrees.set(node.id, computeInDegree(node.id, ast.edges));
266
+ outDegrees.set(node.id, computeOutDegree(node.id, ast.edges));
267
+ rejections.set(node.id, 0);
268
+ }
269
+ for (const e of ast.edges) {
270
+ if (e.valence === "negative") {
271
+ rejections.set(e.to, (rejections.get(e.to) ?? 0) + 1);
272
+ }
273
+ }
274
+ const inVals = Array.from(inDegrees.values());
275
+ const meanIn = inVals.reduce((a, b) => a + b, 0) / (inVals.length || 1);
276
+ const sdIn = Math.sqrt(
277
+ inVals.reduce((sum, v) => sum + (v - meanIn) ** 2, 0) / (inVals.length || 1)
278
+ );
279
+ const sortedNodes = [...ast.nodes];
280
+ if (ast.groups.length > 0) {
281
+ const groupOrder = /* @__PURE__ */ new Map();
282
+ ast.groups.forEach((g, i) => {
283
+ for (const m of g.members) groupOrder.set(m, i);
284
+ });
285
+ sortedNodes.sort((a, b) => {
286
+ const ga = groupOrder.get(a.id) ?? 999;
287
+ const gb = groupOrder.get(b.id) ?? 999;
288
+ if (ga !== gb) return ga - gb;
289
+ return a.id.localeCompare(b.id);
290
+ });
291
+ }
292
+ const angleStep = 2 * Math.PI / n;
293
+ const startAngle = -Math.PI / 2;
294
+ const layoutNodes = sortedNodes.map((node, i) => {
295
+ const angle = startAngle + i * angleStep;
296
+ const inDeg = inDegrees.get(node.id) ?? 0;
297
+ const outDeg = outDegrees.get(node.id) ?? 0;
298
+ const rej = rejections.get(node.id) ?? 0;
299
+ const nodeRadius = computeNodeRadius(node, inDeg, ast.config.sizing);
300
+ const computedRole = autoDetectRole(node, inDeg, outDeg, meanIn, sdIn, rej);
301
+ return {
302
+ node,
303
+ x: cx + radius * Math.cos(angle),
304
+ y: cy + radius * Math.sin(angle),
305
+ radius: nodeRadius,
306
+ computedRole
307
+ };
308
+ });
309
+ const layoutEdges = computeEdgePositions(layoutNodes, ast.edges);
310
+ return {
311
+ width: canvasSize,
312
+ height: canvasSize,
313
+ nodes: layoutNodes,
314
+ edges: layoutEdges,
315
+ ast
316
+ };
317
+ }
318
+ function layoutForceDirected(ast) {
319
+ const n = ast.nodes.length;
320
+ const canvasSize = n <= 8 ? 400 : n <= 20 ? 600 : n <= 40 ? 800 : 1e3;
321
+ const padding = 60;
322
+ const inDegrees = /* @__PURE__ */ new Map();
323
+ const outDegrees = /* @__PURE__ */ new Map();
324
+ const rejections = /* @__PURE__ */ new Map();
325
+ for (const node of ast.nodes) {
326
+ inDegrees.set(node.id, computeInDegree(node.id, ast.edges));
327
+ outDegrees.set(node.id, computeOutDegree(node.id, ast.edges));
328
+ rejections.set(node.id, 0);
329
+ }
330
+ for (const e of ast.edges) {
331
+ if (e.valence === "negative") {
332
+ rejections.set(e.to, (rejections.get(e.to) ?? 0) + 1);
333
+ }
334
+ }
335
+ const inVals = Array.from(inDegrees.values());
336
+ const meanIn = inVals.reduce((a, b) => a + b, 0) / (inVals.length || 1);
337
+ const sdIn = Math.sqrt(
338
+ inVals.reduce((sum, v) => sum + (v - meanIn) ** 2, 0) / (inVals.length || 1)
339
+ );
340
+ const initRadius = canvasSize * 0.3;
341
+ const cx = canvasSize / 2;
342
+ const cy = canvasSize / 2;
343
+ const positions = ast.nodes.map((_, i) => {
344
+ const angle = 2 * Math.PI * i / n - Math.PI / 2;
345
+ return {
346
+ x: cx + initRadius * Math.cos(angle),
347
+ y: cy + initRadius * Math.sin(angle)
348
+ };
349
+ });
350
+ const nodeIdx = /* @__PURE__ */ new Map();
351
+ ast.nodes.forEach((node, i) => nodeIdx.set(node.id, i));
352
+ const area = (canvasSize - 2 * padding) ** 2;
353
+ const k = Math.sqrt(area / Math.max(n, 1));
354
+ const maxIterations = 200;
355
+ let temperature = canvasSize / 8;
356
+ const coolingRate = 0.95;
357
+ const minDist = 60;
358
+ for (let iter = 0; iter < maxIterations; iter++) {
359
+ const disp = positions.map(() => ({ dx: 0, dy: 0 }));
360
+ for (let i = 0; i < n; i++) {
361
+ for (let j = i + 1; j < n; j++) {
362
+ const dx = positions[i].x - positions[j].x;
363
+ const dy = positions[i].y - positions[j].y;
364
+ const dist = Math.max(Math.sqrt(dx * dx + dy * dy), 1);
365
+ const force = k * k / dist;
366
+ const fx = dx / dist * force;
367
+ const fy = dy / dist * force;
368
+ disp[i].dx += fx;
369
+ disp[i].dy += fy;
370
+ disp[j].dx -= fx;
371
+ disp[j].dy -= fy;
372
+ }
373
+ }
374
+ for (const edge of ast.edges) {
375
+ const i = nodeIdx.get(edge.from);
376
+ const j = nodeIdx.get(edge.to);
377
+ if (i === void 0 || j === void 0) continue;
378
+ const dx = positions[i].x - positions[j].x;
379
+ const dy = positions[i].y - positions[j].y;
380
+ const dist = Math.max(Math.sqrt(dx * dx + dy * dy), 1);
381
+ const force = dist * dist / k;
382
+ const fx = dx / dist * force;
383
+ const fy = dy / dist * force;
384
+ disp[i].dx -= fx;
385
+ disp[i].dy -= fy;
386
+ disp[j].dx += fx;
387
+ disp[j].dy += fy;
388
+ }
389
+ for (let i = 0; i < n; i++) {
390
+ const mag = Math.sqrt(disp[i].dx ** 2 + disp[i].dy ** 2);
391
+ if (mag > 0) {
392
+ const scale = Math.min(mag, temperature) / mag;
393
+ positions[i].x += disp[i].dx * scale;
394
+ positions[i].y += disp[i].dy * scale;
395
+ }
396
+ positions[i].x = Math.max(padding, Math.min(canvasSize - padding, positions[i].x));
397
+ positions[i].y = Math.max(padding, Math.min(canvasSize - padding, positions[i].y));
398
+ }
399
+ for (let i = 0; i < n; i++) {
400
+ for (let j = i + 1; j < n; j++) {
401
+ const dx = positions[i].x - positions[j].x;
402
+ const dy = positions[i].y - positions[j].y;
403
+ const dist = Math.sqrt(dx * dx + dy * dy);
404
+ if (dist < minDist && dist > 0) {
405
+ const push = (minDist - dist) / 2;
406
+ const nx = dx / dist;
407
+ const ny = dy / dist;
408
+ positions[i].x += nx * push;
409
+ positions[i].y += ny * push;
410
+ positions[j].x -= nx * push;
411
+ positions[j].y -= ny * push;
412
+ }
413
+ }
414
+ }
415
+ temperature *= coolingRate;
416
+ }
417
+ const layoutNodes = ast.nodes.map((node, i) => {
418
+ const inDeg = inDegrees.get(node.id) ?? 0;
419
+ const outDeg = outDegrees.get(node.id) ?? 0;
420
+ const rej = rejections.get(node.id) ?? 0;
421
+ const nodeRadius = computeNodeRadius(node, inDeg, ast.config.sizing);
422
+ const computedRole = autoDetectRole(node, inDeg, outDeg, meanIn, sdIn, rej);
423
+ return {
424
+ node,
425
+ x: positions[i].x,
426
+ y: positions[i].y,
427
+ radius: nodeRadius,
428
+ computedRole
429
+ };
430
+ });
431
+ const layoutEdges = computeEdgePositions(layoutNodes, ast.edges);
432
+ return {
433
+ width: canvasSize,
434
+ height: canvasSize,
435
+ nodes: layoutNodes,
436
+ edges: layoutEdges,
437
+ ast
438
+ };
439
+ }
440
+ function computeEdgePositions(nodes, edges) {
441
+ const nodeMap = /* @__PURE__ */ new Map();
442
+ for (const n of nodes) nodeMap.set(n.node.id, n);
443
+ return edges.map((edge) => {
444
+ const fromNode = nodeMap.get(edge.from);
445
+ const toNode = nodeMap.get(edge.to);
446
+ if (!fromNode || !toNode) {
447
+ return { edge, x1: 0, y1: 0, x2: 0, y2: 0 };
448
+ }
449
+ const dx = toNode.x - fromNode.x;
450
+ const dy = toNode.y - fromNode.y;
451
+ const dist = Math.sqrt(dx * dx + dy * dy);
452
+ if (dist === 0) return { edge, x1: fromNode.x, y1: fromNode.y, x2: toNode.x, y2: toNode.y };
453
+ const nx = dx / dist;
454
+ const ny = dy / dist;
455
+ const markerGap = 10;
456
+ return {
457
+ edge,
458
+ x1: fromNode.x + nx * (fromNode.radius + 2),
459
+ y1: fromNode.y + ny * (fromNode.radius + 2),
460
+ x2: toNode.x - nx * (toNode.radius + markerGap),
461
+ y2: toNode.y - ny * (toNode.radius + markerGap)
462
+ };
463
+ });
464
+ }
465
+ function layoutSociogram(ast) {
466
+ switch (ast.config.layout) {
467
+ case "force-directed":
468
+ return layoutForceDirected(ast);
469
+ case "circular":
470
+ default:
471
+ return layoutCircular(ast);
472
+ }
473
+ }
474
+
475
+ // src/diagrams/sociogram/renderer.ts
476
+ var LABEL_GAP = 4;
477
+ var LABEL_FONT_SIZE = 11;
478
+ function valenceColors(t) {
479
+ return { positive: t.positive, negative: t.negative, neutral: t.neutral };
480
+ }
481
+ function roleFills(t) {
482
+ return { star: t.warn, isolate: t.fillMuted, rejected: t.negative };
483
+ }
484
+ function buildCSS(ast, t) {
485
+ const vc = valenceColors(t);
486
+ const rf = roleFills(t);
487
+ const groupColors = ast.groups.map((g, i) => {
488
+ const color = g.color ?? t.palette[i % t.palette.length];
489
+ return `.schematex-sociogram-group-${g.id} { fill: ${color}; stroke: ${color}; }`;
490
+ });
491
+ return `
492
+ .schematex-sociogram {${cssCustomProperties(t)}
493
+ font-family: system-ui, -apple-system, sans-serif;
494
+ background: ${t.bg};
495
+ }
496
+ .schematex-sociogram-node { fill: ${t.accent}; stroke: ${t.accent}; stroke-width: ${STROKE_WIDTH.normal}; }
497
+ .schematex-sociogram-node-star { fill: ${rf.star}; stroke: ${t.warn}; stroke-width: ${STROKE_WIDTH.thick}; }
498
+ .schematex-sociogram-node-isolate { fill: ${rf.isolate}; stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 4 3; }
499
+ .schematex-sociogram-node-neglectee { fill: ${t.fillMuted}; stroke: ${t.accent}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 4 3; }
500
+ .schematex-sociogram-node-rejected { fill: ${rf.rejected}; stroke: ${t.negative}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 4 3; }
501
+ .schematex-sociogram-edge { stroke-linecap: round; }
502
+ .schematex-sociogram-edge-positive { stroke: ${vc.positive}; }
503
+ .schematex-sociogram-edge-negative { stroke: ${vc.negative}; stroke-dasharray: 6 3; }
504
+ .schematex-sociogram-edge-neutral { stroke: ${vc.neutral}; stroke-dasharray: 2 3; }
505
+ .schematex-sociogram-label { font-size: ${LABEL_FONT_SIZE}px; fill: ${t.text}; text-anchor: middle; }
506
+ .schematex-sociogram-edge-label { font-size: ${FONT_SIZE.small}px; fill: ${t.textMuted}; text-anchor: middle; }
507
+ .schematex-sociogram-title { font-size: ${FONT_SIZE.title}px; font-weight: bold; fill: ${t.text}; text-anchor: middle; }
508
+ .schematex-sociogram-star-badge { font-size: 10px; fill: ${t.warn}; }
509
+ .schematex-sociogram-group-label { font-size: 13px; font-weight: bold; fill-opacity: 0.7; text-anchor: middle; }
510
+ ${groupColors.join("\n")}
511
+ `.trim();
512
+ }
513
+ function buildDefs(t) {
514
+ const vc = valenceColors(t);
515
+ const markers = [
516
+ { id: "sociogram-arrow", fill: vc.positive },
517
+ { id: "sociogram-arrow-negative", fill: vc.negative },
518
+ { id: "sociogram-arrow-neutral", fill: vc.neutral }
519
+ ];
520
+ const markerEls = markers.map(
521
+ (m) => el(
522
+ "marker",
523
+ {
524
+ id: m.id,
525
+ viewBox: "0 0 10 10",
526
+ refX: "9",
527
+ refY: "5",
528
+ markerWidth: "8",
529
+ markerHeight: "8",
530
+ orient: "auto"
531
+ },
532
+ el("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: m.fill })
533
+ )
534
+ );
535
+ return el("defs", {}, markerEls);
536
+ }
537
+ function edgeStrokeWidth(weight) {
538
+ if (weight <= 1) return 1;
539
+ if (weight === 2) return 2;
540
+ if (weight === 3) return 3.5;
541
+ return 5;
542
+ }
543
+ function getNodeClass(role) {
544
+ if (!role) return "schematex-sociogram-node";
545
+ switch (role) {
546
+ case "star":
547
+ return "schematex-sociogram-node schematex-sociogram-node-star";
548
+ case "isolate":
549
+ return "schematex-sociogram-node schematex-sociogram-node-isolate";
550
+ case "neglectee":
551
+ return "schematex-sociogram-node schematex-sociogram-node-neglectee";
552
+ case "rejected":
553
+ return "schematex-sociogram-node schematex-sociogram-node-rejected";
554
+ case "bridge":
555
+ return "schematex-sociogram-node";
556
+ default:
557
+ return "schematex-sociogram-node";
558
+ }
559
+ }
560
+ function getNodeFill(node, ast, t) {
561
+ const role = node.computedRole ?? node.node.role;
562
+ const rf = roleFills(t);
563
+ if (ast.config.coloring === "role" && role) {
564
+ return rf[role];
565
+ }
566
+ if (ast.config.coloring === "group" && node.node.group) {
567
+ const gIdx = ast.groups.findIndex((g) => g.id === node.node.group);
568
+ if (gIdx >= 0) {
569
+ return ast.groups[gIdx].color ?? t.palette[gIdx % t.palette.length];
570
+ }
571
+ }
572
+ return void 0;
573
+ }
574
+ function renderNodes(layout, t) {
575
+ const nodeEls = [];
576
+ const labelEls = [];
577
+ const { ast } = layout;
578
+ for (const layoutNode of layout.nodes) {
579
+ const { node, x, y, radius, computedRole } = layoutNode;
580
+ const role = computedRole ?? node.role;
581
+ const cls = getNodeClass(role);
582
+ const fill = getNodeFill(layoutNode, ast, t);
583
+ const attrs = {
584
+ cx: x,
585
+ cy: y,
586
+ r: radius,
587
+ class: cls,
588
+ "data-node-id": node.id
589
+ };
590
+ if (fill) attrs.fill = fill;
591
+ nodeEls.push(circle(attrs));
592
+ if (role === "star") {
593
+ const badgeX = x + radius * 0.6;
594
+ const badgeY = y - radius * 0.6;
595
+ const s = 6;
596
+ const points = starPoints(badgeX, badgeY, s, s * 0.4, 5);
597
+ nodeEls.push(
598
+ polygon({
599
+ points,
600
+ fill: t.warn,
601
+ class: "schematex-sociogram-star-badge"
602
+ })
603
+ );
604
+ }
605
+ const label = node.label ?? node.id;
606
+ labelEls.push(
607
+ text(
608
+ {
609
+ x,
610
+ y: y + radius + LABEL_GAP + LABEL_FONT_SIZE,
611
+ class: "schematex-sociogram-label",
612
+ "data-node-id": node.id
613
+ },
614
+ label
615
+ )
616
+ );
617
+ }
618
+ return { nodeEls, labelEls };
619
+ }
620
+ function starPoints(cx, cy, outerR, innerR, points) {
621
+ const coords = [];
622
+ for (let i = 0; i < points * 2; i++) {
623
+ const angle = Math.PI * i / points - Math.PI / 2;
624
+ const r = i % 2 === 0 ? outerR : innerR;
625
+ coords.push(`${cx + r * Math.cos(angle)},${cy + r * Math.sin(angle)}`);
626
+ }
627
+ return coords.join(" ");
628
+ }
629
+ function getMarkerUrl(valence, direction) {
630
+ if (direction === "undirected") return {};
631
+ const markerId = valence === "negative" ? "sociogram-arrow-negative" : valence === "neutral" ? "sociogram-arrow-neutral" : "sociogram-arrow";
632
+ if (direction === "mutual") {
633
+ return {
634
+ start: `url(#${markerId})`,
635
+ end: `url(#${markerId})`
636
+ };
637
+ }
638
+ return { end: `url(#${markerId})` };
639
+ }
640
+ function renderEdges(layout) {
641
+ const edgeEls = [];
642
+ const edgeLabelEls = [];
643
+ for (const layoutEdge of layout.edges) {
644
+ const { edge, x1, y1, x2, y2 } = layoutEdge;
645
+ const valenceClass = `schematex-sociogram-edge-${edge.valence}`;
646
+ const markers = getMarkerUrl(edge.valence, edge.direction);
647
+ const sw = edgeStrokeWidth(edge.weight);
648
+ const attrs = {
649
+ x1,
650
+ y1,
651
+ x2,
652
+ y2,
653
+ class: `schematex-sociogram-edge ${valenceClass}`,
654
+ "stroke-width": sw,
655
+ "data-from": edge.from,
656
+ "data-to": edge.to
657
+ };
658
+ if (markers.end) attrs["marker-end"] = markers.end;
659
+ if (markers.start) attrs["marker-start"] = markers.start;
660
+ edgeEls.push(line(attrs));
661
+ if (edge.label) {
662
+ const mx = (x1 + x2) / 2;
663
+ const my = (y1 + y2) / 2;
664
+ edgeLabelEls.push(
665
+ text(
666
+ { x: mx, y: my - 6, class: "schematex-sociogram-edge-label" },
667
+ edge.label
668
+ )
669
+ );
670
+ }
671
+ }
672
+ return { edgeEls, edgeLabelEls };
673
+ }
674
+ function renderGroupLabels(layout, t) {
675
+ const elements = [];
676
+ const { ast } = layout;
677
+ for (let gi = 0; gi < ast.groups.length; gi++) {
678
+ const grp = ast.groups[gi];
679
+ const memberNodes = layout.nodes.filter(
680
+ (n) => n.node.group === grp.id
681
+ );
682
+ if (memberNodes.length === 0) continue;
683
+ const color = grp.color ?? t.palette[gi % t.palette.length];
684
+ const cx = memberNodes.reduce((s, n) => s + n.x, 0) / memberNodes.length;
685
+ const minY = Math.min(...memberNodes.map((n) => n.y - n.radius));
686
+ elements.push(
687
+ text(
688
+ {
689
+ x: cx,
690
+ y: minY - 14,
691
+ class: "schematex-sociogram-group-label",
692
+ fill: color
693
+ },
694
+ grp.label ?? grp.id
695
+ )
696
+ );
697
+ }
698
+ return elements;
699
+ }
700
+ function renderSociogram(layout, options = {}) {
701
+ const { ast } = layout;
702
+ const t = resolveBaseTheme(options.theme ?? "default");
703
+ const css = buildCSS(ast, t);
704
+ const defsStr = buildDefs(t);
705
+ const titleOffset = ast.title ? 30 : 0;
706
+ const totalWidth = layout.width;
707
+ const totalHeight = layout.height + titleOffset;
708
+ const { edgeEls, edgeLabelEls } = renderEdges(layout);
709
+ const { nodeEls, labelEls } = renderNodes(layout, t);
710
+ const groupLabelEls = renderGroupLabels(layout, t);
711
+ const titleEl = ast.title ? text(
712
+ { x: totalWidth / 2, y: 20, class: "schematex-sociogram-title" },
713
+ ast.title
714
+ ) : "";
715
+ const transformY = titleOffset;
716
+ const transform = transformY ? `translate(0,${transformY})` : void 0;
717
+ const svgContent = [
718
+ title(`Sociogram${ast.title ? `: ${ast.title}` : ""}`),
719
+ desc(
720
+ `Sociogram with ${ast.nodes.length} members and ${ast.edges.length} connections`
721
+ ),
722
+ el("style", {}, css),
723
+ defsStr
724
+ ];
725
+ if (titleEl) svgContent.push(titleEl);
726
+ if (groupLabelEls.length > 0) {
727
+ svgContent.push(
728
+ group({ class: "schematex-sociogram-groups", transform }, groupLabelEls)
729
+ );
730
+ }
731
+ svgContent.push(
732
+ group({ class: "schematex-sociogram-edges", transform }, [
733
+ ...edgeEls,
734
+ ...edgeLabelEls
735
+ ])
736
+ );
737
+ svgContent.push(
738
+ group({ class: "schematex-sociogram-nodes", transform }, nodeEls)
739
+ );
740
+ svgContent.push(
741
+ group({ class: "schematex-sociogram-labels", transform }, labelEls)
742
+ );
743
+ return svgRoot(
744
+ {
745
+ class: "schematex-diagram schematex-sociogram",
746
+ viewBox: `0 0 ${totalWidth} ${totalHeight}`,
747
+ width: totalWidth,
748
+ height: totalHeight
749
+ },
750
+ svgContent
751
+ );
752
+ }
753
+
754
+ // src/diagrams/sociogram/index.ts
755
+ var sociogram = {
756
+ type: "sociogram",
757
+ detect(text2) {
758
+ const first = text2.trim().split("\n")[0]?.trim().toLowerCase() ?? "";
759
+ return first.startsWith("sociogram");
760
+ },
761
+ render(text2, config) {
762
+ const ast = parseSociogram(text2);
763
+ const layout = layoutSociogram(ast);
764
+ return renderSociogram(layout, { theme: config?.theme });
765
+ }
766
+ };
767
+
768
+ export { layoutSociogram, parseSociogram, renderSociogram, sociogram };
769
+ //# sourceMappingURL=chunk-PDPHRZZT.js.map
770
+ //# sourceMappingURL=chunk-PDPHRZZT.js.map