schematex 0.3.0 → 0.3.2

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 (185) hide show
  1. package/dist/ai/ai-sdk.cjs +25 -25
  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 +20 -20
  5. package/dist/ai/index.cjs +28 -28
  6. package/dist/ai/index.d.cts +1 -1
  7. package/dist/ai/index.d.ts +1 -1
  8. package/dist/ai/index.js +20 -20
  9. package/dist/browser.cjs +22 -22
  10. package/dist/browser.js +20 -20
  11. package/dist/{chunk-TYCHEOQX.js → chunk-2J2QWNGI.js} +26 -20
  12. package/dist/chunk-2J2QWNGI.js.map +1 -0
  13. package/dist/{chunk-MCFQAUQV.cjs → chunk-2UJAVPA4.cjs} +58 -39
  14. package/dist/chunk-2UJAVPA4.cjs.map +1 -0
  15. package/dist/{chunk-5AEN2PLB.cjs → chunk-3YXXZ4LT.cjs} +33 -33
  16. package/dist/{chunk-5AEN2PLB.cjs.map → chunk-3YXXZ4LT.cjs.map} +1 -1
  17. package/dist/{chunk-RP5UATRA.js → chunk-56LXBM45.js} +4 -4
  18. package/dist/{chunk-RP5UATRA.js.map → chunk-56LXBM45.js.map} +1 -1
  19. package/dist/{chunk-OC22GGQN.js → chunk-6BKUD5EJ.js} +54 -4
  20. package/dist/chunk-6BKUD5EJ.js.map +1 -0
  21. package/dist/{chunk-ULYRO2KY.cjs → chunk-75BMFCP5.cjs} +44 -44
  22. package/dist/{chunk-ULYRO2KY.cjs.map → chunk-75BMFCP5.cjs.map} +1 -1
  23. package/dist/{chunk-ZNDIGQJD.js → chunk-7BEJHG43.js} +3 -3
  24. package/dist/{chunk-ZNDIGQJD.js.map → chunk-7BEJHG43.js.map} +1 -1
  25. package/dist/{chunk-WYFXOXVK.cjs → chunk-7MVDN5UC.cjs} +35 -35
  26. package/dist/{chunk-WYFXOXVK.cjs.map → chunk-7MVDN5UC.cjs.map} +1 -1
  27. package/dist/{chunk-JDTB7IKL.js → chunk-BJ65PKDU.js} +3 -3
  28. package/dist/{chunk-JDTB7IKL.js.map → chunk-BJ65PKDU.js.map} +1 -1
  29. package/dist/{chunk-FE6GAUNW.js → chunk-BJWMPPEA.js} +5 -5
  30. package/dist/{chunk-FE6GAUNW.js.map → chunk-BJWMPPEA.js.map} +1 -1
  31. package/dist/{chunk-MJGDP3CS.cjs → chunk-BUN3CRMP.cjs} +488 -109
  32. package/dist/chunk-BUN3CRMP.cjs.map +1 -0
  33. package/dist/{chunk-COLTVQWR.cjs → chunk-C3IVD7DI.cjs} +25 -25
  34. package/dist/{chunk-COLTVQWR.cjs.map → chunk-C3IVD7DI.cjs.map} +1 -1
  35. package/dist/{chunk-3YZ6FPQW.cjs → chunk-EYHD7LV3.cjs} +131 -131
  36. package/dist/{chunk-3YZ6FPQW.cjs.map → chunk-EYHD7LV3.cjs.map} +1 -1
  37. package/dist/{chunk-UGCUNADI.js → chunk-F6OROIHS.js} +35 -16
  38. package/dist/chunk-F6OROIHS.js.map +1 -0
  39. package/dist/{chunk-3FKS4KQK.cjs → chunk-GYTWJ6VB.cjs} +65 -59
  40. package/dist/chunk-GYTWJ6VB.cjs.map +1 -0
  41. package/dist/{chunk-VLMK7MQK.js → chunk-H4CJTKEH.js} +23 -4
  42. package/dist/chunk-H4CJTKEH.js.map +1 -0
  43. package/dist/{chunk-45KP67RR.js → chunk-HIQPEAL7.js} +5 -5
  44. package/dist/chunk-HIQPEAL7.js.map +1 -0
  45. package/dist/{chunk-NB56L5QK.js → chunk-IT2TVXC7.js} +4 -4
  46. package/dist/{chunk-NB56L5QK.js.map → chunk-IT2TVXC7.js.map} +1 -1
  47. package/dist/{chunk-2JDVJRR3.cjs → chunk-K5QG53GT.cjs} +28 -28
  48. package/dist/chunk-K5QG53GT.cjs.map +1 -0
  49. package/dist/{chunk-B37IKTI7.cjs → chunk-KUXOHLGC.cjs} +97 -47
  50. package/dist/chunk-KUXOHLGC.cjs.map +1 -0
  51. package/dist/{chunk-A5D2IMOX.cjs → chunk-L2KUGWFR.cjs} +47 -47
  52. package/dist/{chunk-A5D2IMOX.cjs.map → chunk-L2KUGWFR.cjs.map} +1 -1
  53. package/dist/{chunk-HDKDQAEQ.cjs → chunk-LFZZ4NCP.cjs} +14 -2
  54. package/dist/chunk-LFZZ4NCP.cjs.map +1 -0
  55. package/dist/{chunk-U6L3FAML.js → chunk-M3R6RCXY.js} +3 -3
  56. package/dist/{chunk-U6L3FAML.js.map → chunk-M3R6RCXY.js.map} +1 -1
  57. package/dist/{chunk-5YYAYW67.js → chunk-M5ZC3LFJ.js} +3 -3
  58. package/dist/{chunk-5YYAYW67.js.map → chunk-M5ZC3LFJ.js.map} +1 -1
  59. package/dist/{chunk-B6INLQBU.cjs → chunk-MKKFIPKU.cjs} +46 -46
  60. package/dist/{chunk-B6INLQBU.cjs.map → chunk-MKKFIPKU.cjs.map} +1 -1
  61. package/dist/{chunk-M5B2UUNW.js → chunk-NFT6VW73.js} +4 -4
  62. package/dist/{chunk-M5B2UUNW.js.map → chunk-NFT6VW73.js.map} +1 -1
  63. package/dist/{chunk-X7RPFTTR.cjs → chunk-OK5RYX55.cjs} +26 -26
  64. package/dist/{chunk-X7RPFTTR.cjs.map → chunk-OK5RYX55.cjs.map} +1 -1
  65. package/dist/{chunk-NNU4RGT3.js → chunk-RNGYXGHS.js} +39 -27
  66. package/dist/chunk-RNGYXGHS.js.map +1 -0
  67. package/dist/{chunk-SQKLKBBK.cjs → chunk-RODV6PC4.cjs} +25 -6
  68. package/dist/chunk-RODV6PC4.cjs.map +1 -0
  69. package/dist/{chunk-H2OEUBPO.js → chunk-SE23X5OE.js} +441 -62
  70. package/dist/chunk-SE23X5OE.js.map +1 -0
  71. package/dist/{chunk-FCGHV6ZK.js → chunk-SFSZUOFT.js} +4 -4
  72. package/dist/{chunk-FCGHV6ZK.js.map → chunk-SFSZUOFT.js.map} +1 -1
  73. package/dist/{chunk-NZH4GWE6.cjs → chunk-UK7JF5QB.cjs} +37 -37
  74. package/dist/{chunk-NZH4GWE6.cjs.map → chunk-UK7JF5QB.cjs.map} +1 -1
  75. package/dist/{chunk-QSQX77S2.cjs → chunk-W7GIQTJV.cjs} +21 -21
  76. package/dist/{chunk-QSQX77S2.cjs.map → chunk-W7GIQTJV.cjs.map} +1 -1
  77. package/dist/{chunk-KLJEK547.js → chunk-WHJXRLFD.js} +14 -3
  78. package/dist/chunk-WHJXRLFD.js.map +1 -0
  79. package/dist/{chunk-YQANC7HQ.js → chunk-X4F6VVEJ.js} +3 -3
  80. package/dist/{chunk-YQANC7HQ.js.map → chunk-X4F6VVEJ.js.map} +1 -1
  81. package/dist/{chunk-ZNOD4VZT.cjs → chunk-XI6JOG76.cjs} +46 -46
  82. package/dist/{chunk-ZNOD4VZT.cjs.map → chunk-XI6JOG76.cjs.map} +1 -1
  83. package/dist/{chunk-K2SOC3XF.cjs → chunk-XUEROLSB.cjs} +70 -19
  84. package/dist/chunk-XUEROLSB.cjs.map +1 -0
  85. package/dist/{chunk-XTATRNUN.cjs → chunk-YTEEZV6J.cjs} +442 -430
  86. package/dist/chunk-YTEEZV6J.cjs.map +1 -0
  87. package/dist/{chunk-XZNPAD6E.js → chunk-ZNLEUL7T.js} +58 -7
  88. package/dist/chunk-ZNLEUL7T.js.map +1 -0
  89. package/dist/{chunk-DNZFOCV7.js → chunk-ZTSO3S4P.js} +3 -3
  90. package/dist/{chunk-DNZFOCV7.js.map → chunk-ZTSO3S4P.js.map} +1 -1
  91. package/dist/diagrams/blockdiagram/index.cjs +6 -6
  92. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  93. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  94. package/dist/diagrams/blockdiagram/index.js +2 -2
  95. package/dist/diagrams/circuit/index.cjs +8 -8
  96. package/dist/diagrams/circuit/index.d.cts +1 -1
  97. package/dist/diagrams/circuit/index.d.ts +1 -1
  98. package/dist/diagrams/circuit/index.js +2 -2
  99. package/dist/diagrams/ecomap/index.cjs +8 -8
  100. package/dist/diagrams/ecomap/index.d.cts +1 -1
  101. package/dist/diagrams/ecomap/index.d.ts +1 -1
  102. package/dist/diagrams/ecomap/index.js +3 -3
  103. package/dist/diagrams/entity/index.cjs +6 -6
  104. package/dist/diagrams/entity/index.d.cts +1 -1
  105. package/dist/diagrams/entity/index.d.ts +1 -1
  106. package/dist/diagrams/entity/index.js +2 -2
  107. package/dist/diagrams/fishbone/index.cjs +8 -8
  108. package/dist/diagrams/fishbone/index.d.cts +1 -1
  109. package/dist/diagrams/fishbone/index.d.ts +1 -1
  110. package/dist/diagrams/fishbone/index.js +2 -2
  111. package/dist/diagrams/flowchart/index.cjs +8 -8
  112. package/dist/diagrams/flowchart/index.d.cts +2 -2
  113. package/dist/diagrams/flowchart/index.d.ts +2 -2
  114. package/dist/diagrams/flowchart/index.js +2 -2
  115. package/dist/diagrams/genogram/index.cjs +10 -10
  116. package/dist/diagrams/genogram/index.d.cts +1 -1
  117. package/dist/diagrams/genogram/index.d.ts +1 -1
  118. package/dist/diagrams/genogram/index.js +3 -3
  119. package/dist/diagrams/ladder/index.cjs +6 -6
  120. package/dist/diagrams/ladder/index.d.cts +1 -1
  121. package/dist/diagrams/ladder/index.d.ts +1 -1
  122. package/dist/diagrams/ladder/index.js +2 -2
  123. package/dist/diagrams/logic/index.cjs +6 -6
  124. package/dist/diagrams/logic/index.d.cts +1 -1
  125. package/dist/diagrams/logic/index.d.ts +1 -1
  126. package/dist/diagrams/logic/index.js +2 -2
  127. package/dist/diagrams/orgchart/index.cjs +7 -7
  128. package/dist/diagrams/orgchart/index.d.cts +1 -1
  129. package/dist/diagrams/orgchart/index.d.ts +1 -1
  130. package/dist/diagrams/orgchart/index.js +2 -2
  131. package/dist/diagrams/pedigree/index.cjs +8 -8
  132. package/dist/diagrams/pedigree/index.d.cts +1 -1
  133. package/dist/diagrams/pedigree/index.d.ts +1 -1
  134. package/dist/diagrams/pedigree/index.js +3 -3
  135. package/dist/diagrams/phylo/index.cjs +7 -7
  136. package/dist/diagrams/phylo/index.d.cts +1 -1
  137. package/dist/diagrams/phylo/index.d.ts +1 -1
  138. package/dist/diagrams/phylo/index.js +2 -2
  139. package/dist/diagrams/sld/index.cjs +6 -6
  140. package/dist/diagrams/sld/index.d.cts +1 -1
  141. package/dist/diagrams/sld/index.d.ts +1 -1
  142. package/dist/diagrams/sld/index.js +2 -2
  143. package/dist/diagrams/sociogram/index.cjs +7 -7
  144. package/dist/diagrams/sociogram/index.d.cts +1 -1
  145. package/dist/diagrams/sociogram/index.d.ts +1 -1
  146. package/dist/diagrams/sociogram/index.js +3 -3
  147. package/dist/diagrams/timing/index.cjs +5 -5
  148. package/dist/diagrams/timing/index.d.cts +1 -1
  149. package/dist/diagrams/timing/index.d.ts +1 -1
  150. package/dist/diagrams/timing/index.js +2 -2
  151. package/dist/diagrams/venn/index.cjs +9 -9
  152. package/dist/diagrams/venn/index.d.cts +1 -1
  153. package/dist/diagrams/venn/index.d.ts +1 -1
  154. package/dist/diagrams/venn/index.js +2 -2
  155. package/dist/{index-ivhNGsyU.d.cts → index-BrLxEzSQ.d.cts} +1 -1
  156. package/dist/{index-CUwp4GXI.d.ts → index-dWDwG6BW.d.ts} +1 -1
  157. package/dist/index.cjs +41 -41
  158. package/dist/index.d.cts +2 -2
  159. package/dist/index.d.ts +2 -2
  160. package/dist/index.js +19 -19
  161. package/dist/react.cjs +20 -20
  162. package/dist/react.cjs.map +1 -1
  163. package/dist/react.js +19 -19
  164. package/dist/react.js.map +1 -1
  165. package/dist/{types-Bl-Pn7Wj.d.cts → types-BtiUg7Gx.d.cts} +24 -3
  166. package/dist/{types-Bl-Pn7Wj.d.ts → types-BtiUg7Gx.d.ts} +24 -3
  167. package/package.json +1 -1
  168. package/dist/chunk-2JDVJRR3.cjs.map +0 -1
  169. package/dist/chunk-3FKS4KQK.cjs.map +0 -1
  170. package/dist/chunk-45KP67RR.js.map +0 -1
  171. package/dist/chunk-B37IKTI7.cjs.map +0 -1
  172. package/dist/chunk-H2OEUBPO.js.map +0 -1
  173. package/dist/chunk-HDKDQAEQ.cjs.map +0 -1
  174. package/dist/chunk-K2SOC3XF.cjs.map +0 -1
  175. package/dist/chunk-KLJEK547.js.map +0 -1
  176. package/dist/chunk-MCFQAUQV.cjs.map +0 -1
  177. package/dist/chunk-MJGDP3CS.cjs.map +0 -1
  178. package/dist/chunk-NNU4RGT3.js.map +0 -1
  179. package/dist/chunk-OC22GGQN.js.map +0 -1
  180. package/dist/chunk-SQKLKBBK.cjs.map +0 -1
  181. package/dist/chunk-TYCHEOQX.js.map +0 -1
  182. package/dist/chunk-UGCUNADI.js.map +0 -1
  183. package/dist/chunk-VLMK7MQK.js.map +0 -1
  184. package/dist/chunk-XTATRNUN.cjs.map +0 -1
  185. package/dist/chunk-XZNPAD6E.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { parseLegendDirective, applyLegendOverrides, renderLegend } from './chunk-DNZFOCV7.js';
1
+ import { parseLegendDirective, applyLegendOverrides, renderLegend } from './chunk-ZTSO3S4P.js';
2
2
  import { resolveGenogramTheme, STROKE_WIDTH, cssCustomProperties } from './chunk-2VNMKOUO.js';
3
- import { title, text, group, pattern, path, circle, el, defs, polygon, rect, line, desc, svgRoot } from './chunk-KLJEK547.js';
3
+ import { title, text, group, pattern, path, circle, el, defs, polygon, rect, line, desc, svgRoot } from './chunk-WHJXRLFD.js';
4
4
 
5
5
  // src/diagrams/genogram/parser.ts
6
6
  var ParseError = class extends Error {
@@ -17,6 +17,9 @@ var ParseError = class extends Error {
17
17
  source;
18
18
  };
19
19
  var COUPLE_OPS = [
20
+ { token: "~/~", type: "cohabiting-ended" },
21
+ { token: "-//", type: "separated" },
22
+ // alias for -/-
20
23
  { token: "-x-", type: "divorced" },
21
24
  { token: "-/-", type: "separated" },
22
25
  { token: "-o-", type: "engaged" },
@@ -37,6 +40,7 @@ var SPECIAL_CHILD_PROPS = /* @__PURE__ */ new Set([
37
40
  "twin-identical",
38
41
  "twin-fraternal"
39
42
  ]);
43
+ var SECONDARY_LINK_PROPS = /* @__PURE__ */ new Set(["foster", "adopted", "guardian"]);
40
44
  var VALID_FILLS = /* @__PURE__ */ new Set([
41
45
  "full",
42
46
  "half-left",
@@ -110,6 +114,8 @@ function parseGenogram(text2) {
110
114
  const relationships = [];
111
115
  const childSpecialProps = /* @__PURE__ */ new Map();
112
116
  const legendOverrides = {};
117
+ const childrenWithPrimary = /* @__PURE__ */ new Set();
118
+ let unknownSibCounter = 0;
113
119
  skipBlankAndComments(state);
114
120
  while (state.currentLine < state.lines.length) {
115
121
  skipBlankAndComments(state);
@@ -164,8 +170,12 @@ function parseGenogram(text2) {
164
170
  }
165
171
  const rightKey = rightId.toLowerCase();
166
172
  if (rightProps) {
167
- const rightIndividual = buildIndividual(rightId, rightProps, lineNum, lineText);
168
- individualsMap.set(rightKey, rightIndividual);
173
+ const incoming = buildIndividual(rightId, rightProps, lineNum, lineText);
174
+ const existing = individualsMap.get(rightKey);
175
+ individualsMap.set(
176
+ rightKey,
177
+ existing ? mergeIndividual(existing, incoming, lineNum, lineText) : incoming
178
+ );
169
179
  } else if (!individualsMap.has(rightKey)) {
170
180
  throw new ParseError(
171
181
  `Unknown individual '${rightId}'`,
@@ -189,26 +199,47 @@ function parseGenogram(text2) {
189
199
  const childIndent = getIndent(childLine);
190
200
  if (childIndent <= coupleIndent) break;
191
201
  const childLineNum = state.currentLine + 1;
192
- const { id: childId, propsStr } = splitIdAndProps(childTrimmed);
193
- const childKey = childId.toLowerCase();
194
- const individual = buildIndividual(
195
- childId,
196
- propsStr,
202
+ let parsedId;
203
+ let parsedProps;
204
+ if (childTrimmed === "?" || childTrimmed.startsWith("? ")) {
205
+ parsedId = `__unknown_siblings_${++unknownSibCounter}`;
206
+ parsedProps = "unknown, unknown-siblings";
207
+ } else {
208
+ const split = splitIdAndProps(childTrimmed);
209
+ parsedId = split.id;
210
+ parsedProps = split.propsStr;
211
+ }
212
+ const childKey = parsedId.toLowerCase();
213
+ const incoming = buildIndividual(
214
+ parsedId,
215
+ parsedProps,
197
216
  childLineNum,
198
217
  childLine
199
218
  );
200
- if (propsStr) {
201
- for (const sp of SPECIAL_CHILD_PROPS) {
202
- if (propsTokens(propsStr).includes(sp)) {
203
- childSpecialProps.set(childKey, sp);
204
- }
205
- }
219
+ const tokens = parsedProps ? propsTokens(parsedProps) : [];
220
+ for (const sp of SPECIAL_CHILD_PROPS) {
221
+ if (tokens.includes(sp)) childSpecialProps.set(childKey, sp);
206
222
  }
207
- individualsMap.set(childKey, individual);
223
+ const isSecondaryDecl = tokens.some((t) => SECONDARY_LINK_PROPS.has(t));
224
+ const existing = individualsMap.get(childKey);
225
+ individualsMap.set(
226
+ childKey,
227
+ existing ? mergeIndividual(existing, incoming, childLineNum, childLine) : incoming
228
+ );
208
229
  const coupleKey = `${leftKey}+${rightKey}`;
209
- const pcType = childSpecialProps.get(childKey);
210
- const relType = pcType && (pcType === "adopted" || pcType === "foster") ? pcType : "parent-child";
211
- relationships.push({ type: relType, from: coupleKey, to: childKey });
230
+ const lineChildType = tokens.find(
231
+ (t) => SPECIAL_CHILD_PROPS.has(t)
232
+ );
233
+ const relType = lineChildType === "adopted" || lineChildType === "foster" ? lineChildType : "parent-child";
234
+ const isSecondary = isSecondaryDecl && childrenWithPrimary.has(childKey);
235
+ const rel2 = {
236
+ type: relType,
237
+ from: coupleKey,
238
+ to: childKey
239
+ };
240
+ if (isSecondary) rel2.secondary = true;
241
+ relationships.push(rel2);
242
+ if (!isSecondary) childrenWithPrimary.add(childKey);
212
243
  state.currentLine++;
213
244
  }
214
245
  } else {
@@ -218,7 +249,10 @@ function parseGenogram(text2) {
218
249
  const individual = buildIndividual(id, propsStr, lineNum, lineText);
219
250
  const existing = individualsMap.get(key);
220
251
  if (existing) {
221
- individualsMap.set(key, mergeIndividual(existing, individual));
252
+ individualsMap.set(
253
+ key,
254
+ mergeIndividual(existing, individual, lineNum, lineText)
255
+ );
222
256
  } else {
223
257
  individualsMap.set(key, individual);
224
258
  }
@@ -249,6 +283,7 @@ function parseGenogram(text2) {
249
283
  });
250
284
  }
251
285
  }
286
+ normalizePrimaryParentRels(relationships);
252
287
  const hasLegendOverrides = Object.keys(legendOverrides).length > 0 && Object.values(legendOverrides).some((v) => v !== void 0);
253
288
  return {
254
289
  type: "genogram",
@@ -258,6 +293,33 @@ function parseGenogram(text2) {
258
293
  legendOverrides: hasLegendOverrides ? legendOverrides : void 0
259
294
  };
260
295
  }
296
+ function normalizePrimaryParentRels(relationships) {
297
+ const PARENT_CHILD_TYPES = /* @__PURE__ */ new Set([
298
+ "parent-child",
299
+ "adopted",
300
+ "foster"
301
+ ]);
302
+ const byChild = /* @__PURE__ */ new Map();
303
+ for (let i = 0; i < relationships.length; i++) {
304
+ const r = relationships[i];
305
+ if (!PARENT_CHILD_TYPES.has(r.type)) continue;
306
+ const arr = byChild.get(r.to) ?? [];
307
+ arr.push(i);
308
+ byChild.set(r.to, arr);
309
+ }
310
+ for (const indices of byChild.values()) {
311
+ if (indices.length < 2) continue;
312
+ let primaryIdx = indices.find((i) => relationships[i].type === "parent-child");
313
+ if (primaryIdx === void 0) primaryIdx = indices[0];
314
+ for (const i of indices) {
315
+ if (i === primaryIdx) {
316
+ delete relationships[i].secondary;
317
+ } else {
318
+ relationships[i].secondary = true;
319
+ }
320
+ }
321
+ }
322
+ }
261
323
  function currentLineText(state) {
262
324
  return state.lines[state.currentLine];
263
325
  }
@@ -389,7 +451,11 @@ function buildIndividual(id, propsStr, lineNum, lineText) {
389
451
  } else if (tokenLower === "index") {
390
452
  if (!individual.markers) individual.markers = [];
391
453
  individual.markers.push("index-person");
392
- } else if (SPECIAL_CHILD_PROPS.has(tokenLower)) {
454
+ } else if (tokenLower === "unknown-siblings") {
455
+ if (!individual.markers) individual.markers = [];
456
+ individual.markers.push("unknown-siblings");
457
+ if (!individual.label || individual.label === id) individual.label = "?";
458
+ } else if (SPECIAL_CHILD_PROPS.has(tokenLower) || tokenLower === "guardian") {
393
459
  continue;
394
460
  } else if (/^\d{4}$/.test(tokenLower)) {
395
461
  if (individual.birthYear !== void 0) {
@@ -415,13 +481,27 @@ function buildIndividual(id, propsStr, lineNum, lineText) {
415
481
  if (!isNaN(deathNum)) individual.deathYear = deathNum;
416
482
  } else if (key === "label") {
417
483
  individual.label = value.replace(/^"|"$/g, "");
484
+ } else if (key === "sibling-of") {
485
+ individual.siblingOf = value.toLowerCase();
486
+ } else if (key === "shape") {
487
+ const v = value.toLowerCase();
488
+ if (v === "square" || v === "circle" || v === "diamond" || v === "triangle" || v === "triangle-down") {
489
+ individual.shape = v;
490
+ } else {
491
+ throw new ParseError(
492
+ `Invalid shape '${value}'. Valid: square, circle, diamond, triangle, triangle-down`,
493
+ lineNum,
494
+ 1,
495
+ lineText
496
+ );
497
+ }
418
498
  } else {
419
499
  if (!individual.properties) individual.properties = {};
420
500
  individual.properties[key] = value;
421
501
  }
422
502
  } else {
423
503
  throw new ParseError(
424
- `Unknown property '${token}'. Valid: male, female, unknown, deceased, stillborn, miscarriage, abortion, adopted, foster, twin-identical, twin-fraternal, index, a 4-digit year, conditions:..., age:N, death:YYYY, or key:value`,
504
+ `Unknown property '${token}'. Valid: male, female, unknown, deceased, stillborn, miscarriage, abortion, adopted, foster, guardian, twin-identical, twin-fraternal, index, unknown-siblings, a 4-digit year, conditions:..., age:N, death:YYYY, label:"...", sibling-of:ID, or key:value`,
425
505
  lineNum,
426
506
  1,
427
507
  lineText
@@ -461,20 +541,81 @@ function parseConditions(raw, lineNum, lineText) {
461
541
  }
462
542
  return conditions;
463
543
  }
464
- function mergeIndividual(existing, incoming) {
544
+ function mergeIndividual(existing, incoming, lineNum, lineText) {
545
+ let mergedSex = existing.sex;
546
+ if (incoming.sex !== "unknown") {
547
+ if (existing.sex !== "unknown" && existing.sex !== incoming.sex) {
548
+ throw new ParseError(
549
+ `Conflicting sex for '${existing.id}': previously '${existing.sex}', now '${incoming.sex}'`,
550
+ lineNum,
551
+ 1,
552
+ lineText
553
+ );
554
+ }
555
+ mergedSex = incoming.sex;
556
+ }
557
+ let mergedStatus = existing.status;
558
+ if (incoming.status !== "alive") {
559
+ if (existing.status !== "alive" && existing.status !== incoming.status) {
560
+ throw new ParseError(
561
+ `Conflicting status for '${existing.id}': previously '${existing.status}', now '${incoming.status}'`,
562
+ lineNum,
563
+ 1,
564
+ lineText
565
+ );
566
+ }
567
+ mergedStatus = incoming.status;
568
+ }
569
+ if (incoming.birthYear !== void 0 && existing.birthYear !== void 0 && incoming.birthYear !== existing.birthYear) {
570
+ throw new ParseError(
571
+ `Conflicting birth year for '${existing.id}': previously ${existing.birthYear}, now ${incoming.birthYear}`,
572
+ lineNum,
573
+ 1,
574
+ lineText
575
+ );
576
+ }
577
+ if (incoming.deathYear !== void 0 && existing.deathYear !== void 0 && incoming.deathYear !== existing.deathYear) {
578
+ throw new ParseError(
579
+ `Conflicting death year for '${existing.id}': previously ${existing.deathYear}, now ${incoming.deathYear}`,
580
+ lineNum,
581
+ 1,
582
+ lineText
583
+ );
584
+ }
585
+ const incomingHasExplicitLabel = incoming.label !== incoming.id && incoming.label !== "";
586
+ const existingHasExplicitLabel = existing.label !== existing.id && existing.label !== "";
587
+ const mergedLabel = incomingHasExplicitLabel ? incoming.label : existingHasExplicitLabel ? existing.label : existing.label;
588
+ const mergedMarkers = mergeArrayUnique(existing.markers, incoming.markers);
465
589
  return {
466
590
  ...existing,
467
- sex: incoming.sex !== "unknown" ? incoming.sex : existing.sex,
468
- status: incoming.status !== "alive" ? incoming.status : existing.status,
591
+ sex: mergedSex,
592
+ status: mergedStatus,
593
+ label: mergedLabel,
469
594
  birthYear: incoming.birthYear ?? existing.birthYear,
470
595
  deathYear: incoming.deathYear ?? existing.deathYear,
596
+ age: incoming.age ?? existing.age,
471
597
  conditions: incoming.conditions ?? existing.conditions,
598
+ heritage: incoming.heritage ?? existing.heritage,
599
+ siblingOf: incoming.siblingOf ?? existing.siblingOf,
600
+ markers: mergedMarkers,
472
601
  properties: {
473
602
  ...existing.properties,
474
603
  ...incoming.properties
475
604
  }
476
605
  };
477
606
  }
607
+ function mergeArrayUnique(a, b) {
608
+ if (!a && !b) return void 0;
609
+ const seen = /* @__PURE__ */ new Set();
610
+ const out = [];
611
+ for (const v of [...a ?? [], ...b ?? []]) {
612
+ if (!seen.has(v)) {
613
+ seen.add(v);
614
+ out.push(v);
615
+ }
616
+ }
617
+ return out.length ? out : void 0;
618
+ }
478
619
 
479
620
  // src/diagrams/genogram/layout.ts
480
621
  function layoutGenogram(ast, config) {
@@ -483,8 +624,19 @@ function layoutGenogram(ast, config) {
483
624
  const ordered = orderNodesInGenerations(graph);
484
625
  const positions = assignPositions(ordered, graph, config);
485
626
  const edges = computeEdges(graph, positions, config);
627
+ const secondaryEdges = computeSecondaryParentEdges(
628
+ ast.relationships,
629
+ graph,
630
+ positions,
631
+ config
632
+ );
486
633
  const emotionalEdges = computeEmotionalEdges(ast.relationships, positions, config);
487
- return packageResult(positions, [...edges, ...emotionalEdges], graph, config);
634
+ return packageResult(
635
+ positions,
636
+ [...edges, ...secondaryEdges, ...emotionalEdges],
637
+ graph,
638
+ config
639
+ );
488
640
  }
489
641
  function buildGraph(ast) {
490
642
  const individuals = /* @__PURE__ */ new Map();
@@ -494,7 +646,7 @@ function buildGraph(ast) {
494
646
  const familyUnits = [];
495
647
  const childOf = /* @__PURE__ */ new Map();
496
648
  const coupleRels = ast.relationships.filter(
497
- (r) => r.type === "married" || r.type === "divorced" || r.type === "separated" || r.type === "engaged" || r.type === "cohabiting" || r.type === "consanguineous"
649
+ (r) => r.type === "married" || r.type === "divorced" || r.type === "separated" || r.type === "engaged" || r.type === "cohabiting" || r.type === "cohabiting-ended" || r.type === "consanguineous"
498
650
  );
499
651
  for (const rel of coupleRels) {
500
652
  const fuId = `${rel.from}+${rel.to}`;
@@ -516,7 +668,7 @@ function buildGraph(ast) {
516
668
  }
517
669
  const children = [];
518
670
  for (const r of ast.relationships) {
519
- if ((r.type === "parent-child" || r.type === "adopted" || r.type === "foster") && r.from === fuId) {
671
+ if ((r.type === "parent-child" || r.type === "adopted" || r.type === "foster") && r.from === fuId && !r.secondary) {
520
672
  children.push(r.to);
521
673
  childOf.set(r.to, fuId);
522
674
  }
@@ -539,7 +691,12 @@ function buildGraph(ast) {
539
691
  function assignGenerations(graph) {
540
692
  const { individuals, familyUnits, childOf, generations } = graph;
541
693
  const allIds = Array.from(individuals.keys());
542
- const roots = allIds.filter((id) => !childOf.has(id));
694
+ const roots = allIds.filter((id) => {
695
+ if (childOf.has(id)) return false;
696
+ const ind = individuals.get(id);
697
+ if (ind?.siblingOf) return false;
698
+ return true;
699
+ });
543
700
  if (roots.length === 0 && allIds.length > 0) {
544
701
  for (const id of allIds) generations.set(id, 0);
545
702
  return;
@@ -587,6 +744,21 @@ function assignGenerations(graph) {
587
744
  }
588
745
  }
589
746
  }
747
+ let siblingChanged = true;
748
+ let safety = 0;
749
+ while (siblingChanged && safety++ < allIds.length + 1) {
750
+ siblingChanged = false;
751
+ for (const id of allIds) {
752
+ if (generations.has(id)) continue;
753
+ const ind = individuals.get(id);
754
+ if (!ind?.siblingOf) continue;
755
+ const refGen = generations.get(ind.siblingOf);
756
+ if (refGen !== void 0) {
757
+ generations.set(id, refGen);
758
+ siblingChanged = true;
759
+ }
760
+ }
761
+ }
590
762
  for (const id of allIds) {
591
763
  if (!generations.has(id)) {
592
764
  generations.set(id, 0);
@@ -662,15 +834,33 @@ function orderGeneration(nodeIds, _genIdx, graph, familyUnits) {
662
834
  }
663
835
  }
664
836
  const remaining = nodeIds.filter((id) => !placed.has(id));
665
- remaining.sort((a, b) => {
837
+ const siblingOfRemaining = remaining.filter((id) => {
838
+ const ind = graph.individuals.get(id);
839
+ return ind?.siblingOf && placed.has(ind.siblingOf);
840
+ });
841
+ const otherRemaining = remaining.filter(
842
+ (id) => !siblingOfRemaining.includes(id)
843
+ );
844
+ otherRemaining.sort((a, b) => {
666
845
  const indA = graph.individuals.get(a);
667
846
  const indB = graph.individuals.get(b);
668
847
  return (indA?.birthYear ?? 9999) - (indB?.birthYear ?? 9999);
669
848
  });
670
- for (const id of remaining) {
849
+ for (const id of otherRemaining) {
671
850
  ordered.push(id);
672
851
  placed.add(id);
673
852
  }
853
+ for (const id of siblingOfRemaining) {
854
+ const ind = graph.individuals.get(id);
855
+ if (!ind?.siblingOf) continue;
856
+ const refIdx = ordered.indexOf(ind.siblingOf);
857
+ if (refIdx === -1) {
858
+ ordered.push(id);
859
+ } else {
860
+ ordered.splice(refIdx + 1, 0, id);
861
+ }
862
+ placed.add(id);
863
+ }
674
864
  return ordered;
675
865
  }
676
866
  var LABEL_HEIGHT = 20;
@@ -698,9 +888,10 @@ function assignPositions(orderedGens, graph, config) {
698
888
  }
699
889
  }
700
890
  }
701
- resolveOverlaps(positions, orderedGens, config);
891
+ resolveOverlaps(positions, orderedGens, config, graph);
702
892
  centerChildrenUnderParents(positions, graph, config);
703
- resolveOverlaps(positions, orderedGens, config);
893
+ unscrambleSibships(positions, graph, config);
894
+ resolveOverlaps(positions, orderedGens, config, graph);
704
895
  return positions;
705
896
  }
706
897
  function buildSegments(nodeIds, _genIdx, graph) {
@@ -848,15 +1039,81 @@ function centerChildrenUnderParents(positions, graph, config) {
848
1039
  }
849
1040
  }
850
1041
  }
851
- function resolveOverlaps(positions, orderedGens, config) {
1042
+ function unscrambleSibships(positions, graph, config) {
1043
+ const familyGap = config.nodeWidth + config.nodeSpacingX * 1.5;
1044
+ const childSpacing = config.nodeWidth + config.nodeSpacingX;
1045
+ const byGen = /* @__PURE__ */ new Map();
1046
+ for (const [id, pos] of positions) {
1047
+ const arr = byGen.get(pos.generation) ?? [];
1048
+ arr.push(id);
1049
+ byGen.set(pos.generation, arr);
1050
+ }
1051
+ for (const [, ids] of byGen) {
1052
+ const sibshipMap = /* @__PURE__ */ new Map();
1053
+ for (const id of ids) {
1054
+ const fu = graph.childOf.get(id);
1055
+ if (!fu) continue;
1056
+ const arr = sibshipMap.get(fu) ?? [];
1057
+ arr.push(id);
1058
+ sibshipMap.set(fu, arr);
1059
+ }
1060
+ if (sibshipMap.size <= 1) continue;
1061
+ const sibships = [...sibshipMap.entries()].map(([fuId, children]) => {
1062
+ const fu = graph.familyUnits.find((f) => f.id === fuId);
1063
+ if (!fu) return null;
1064
+ const pa = positions.get(fu.partners[0]);
1065
+ const pb = positions.get(fu.partners[1]);
1066
+ if (!pa || !pb) return null;
1067
+ return { fuId, children, midX: (pa.x + pb.x) / 2 };
1068
+ }).filter((x) => x !== null).sort((a, b) => a.midX - b.midX);
1069
+ let cursor = null;
1070
+ for (const ss of sibships) {
1071
+ const sortedKids = [...ss.children].sort((a, b) => {
1072
+ const ya = graph.individuals.get(a)?.birthYear ?? 9999;
1073
+ const yb = graph.individuals.get(b)?.birthYear ?? 9999;
1074
+ return ya - yb;
1075
+ });
1076
+ const totalWidth = Math.max(0, (sortedKids.length - 1) * childSpacing);
1077
+ let startX = ss.midX - totalWidth / 2;
1078
+ if (cursor !== null && startX < cursor) startX = cursor;
1079
+ for (let i = 0; i < sortedKids.length; i++) {
1080
+ const pos = positions.get(sortedKids[i]);
1081
+ if (pos) pos.x = startX + i * childSpacing;
1082
+ }
1083
+ cursor = startX + totalWidth + familyGap;
1084
+ }
1085
+ }
1086
+ }
1087
+ function resolveOverlaps(positions, orderedGens, config, graph) {
852
1088
  const minGap = config.nodeWidth + config.nodeSpacingX;
1089
+ const familyGap = config.nodeWidth + config.nodeSpacingX * 1.5;
1090
+ const cluster = /* @__PURE__ */ new Map();
1091
+ if (graph) {
1092
+ for (const [id] of positions) {
1093
+ const fu = graph.childOf.get(id);
1094
+ if (fu) cluster.set(id, fu);
1095
+ }
1096
+ for (const fu of graph.familyUnits) {
1097
+ for (const p of fu.partners) {
1098
+ if (cluster.has(p)) continue;
1099
+ const other = fu.partners[0] === p ? fu.partners[1] : fu.partners[0];
1100
+ const otherCluster = cluster.get(other);
1101
+ if (otherCluster) cluster.set(p, otherCluster);
1102
+ }
1103
+ }
1104
+ }
853
1105
  for (const gen of orderedGens) {
854
1106
  const genNodes = gen.nodeIds.map((id) => positions.get(id)).filter((p) => p !== void 0);
855
1107
  genNodes.sort((a, b) => a.x - b.x);
856
1108
  for (let i = 1; i < genNodes.length; i++) {
857
- const gap = genNodes[i].x - genNodes[i - 1].x;
858
- if (gap < minGap) {
859
- const shift = minGap - gap;
1109
+ const prev = genNodes[i - 1];
1110
+ const cur = genNodes[i];
1111
+ const cPrev = cluster.get(prev.id);
1112
+ const cCur = cluster.get(cur.id);
1113
+ const required = cPrev && cCur && cPrev !== cCur ? familyGap : minGap;
1114
+ const gap = cur.x - prev.x;
1115
+ if (gap < required) {
1116
+ const shift = required - gap;
860
1117
  for (let j = i; j < genNodes.length; j++) {
861
1118
  genNodes[j].x += shift;
862
1119
  }
@@ -1089,6 +1346,34 @@ function computeEmotionalEdges(relationships, positions, config) {
1089
1346
  }
1090
1347
  return edges;
1091
1348
  }
1349
+ function computeSecondaryParentEdges(relationships, graph, positions, config) {
1350
+ const edges = [];
1351
+ const half = config.nodeHeight / 2;
1352
+ for (const rel of relationships) {
1353
+ if (!rel.secondary) continue;
1354
+ if (rel.type !== "parent-child" && rel.type !== "foster" && rel.type !== "adopted")
1355
+ continue;
1356
+ const fu = graph.familyUnits.find((f) => f.id === rel.from);
1357
+ if (!fu) continue;
1358
+ const posA = positions.get(fu.partners[0]);
1359
+ const posB = positions.get(fu.partners[1]);
1360
+ const childPos = positions.get(rel.to);
1361
+ if (!posA || !posB || !childPos) continue;
1362
+ const coupleMidX = (posA.x + posB.x) / 2;
1363
+ const coupleY = posA.y;
1364
+ const startX = coupleMidX;
1365
+ const startY = coupleY + half + 4;
1366
+ const childTopY = childPos.y - half - 4;
1367
+ const path2 = childPos.y === coupleY ? `M ${startX} ${startY} L ${startX} ${startY + 16} L ${childPos.x} ${startY + 16} L ${childPos.x} ${childPos.y - half - 4}` : `M ${startX} ${startY} L ${startX} ${(startY + childTopY) / 2} L ${childPos.x} ${(startY + childTopY) / 2} L ${childPos.x} ${childTopY}`;
1368
+ edges.push({
1369
+ from: rel.from,
1370
+ to: rel.to,
1371
+ relationship: rel,
1372
+ path: path2
1373
+ });
1374
+ }
1375
+ return edges;
1376
+ }
1092
1377
  function shiftPath(pathData, dx, dy) {
1093
1378
  return pathData.replace(
1094
1379
  /([\d.-]+)\s+([\d.-]+)/g,
@@ -1115,7 +1400,7 @@ function renderIndividualSymbol(individual, x, y, size) {
1115
1400
  children.push(indexBorder(individual.sex, half));
1116
1401
  classes.push("schematex-genogram-index-person");
1117
1402
  }
1118
- children.push(baseShape(individual.sex, half));
1403
+ children.push(baseShape(individual.sex, half, individual.shape));
1119
1404
  if (individual.conditions?.length) {
1120
1405
  for (const cond of individual.conditions) {
1121
1406
  children.push(conditionFillElement(individual.sex, half, cond));
@@ -1139,6 +1424,23 @@ function renderIndividualSymbol(individual, x, y, size) {
1139
1424
  )
1140
1425
  );
1141
1426
  }
1427
+ const isUnknownSiblings = individual.markers?.includes("unknown-siblings");
1428
+ if (isUnknownSiblings) {
1429
+ classes.push("schematex-genogram-unknown-siblings");
1430
+ children.push(
1431
+ text(
1432
+ {
1433
+ x: 0,
1434
+ y: 5,
1435
+ class: "schematex-genogram-unknown-siblings-mark",
1436
+ "text-anchor": "middle",
1437
+ "font-size": "16",
1438
+ "font-weight": "bold"
1439
+ },
1440
+ "?"
1441
+ )
1442
+ );
1443
+ }
1142
1444
  return group(
1143
1445
  {
1144
1446
  class: classes.join(" "),
@@ -1280,31 +1582,30 @@ function indexBorder(sex, half) {
1280
1582
  });
1281
1583
  }
1282
1584
  }
1283
- function baseShape(sex, half) {
1585
+ function baseShape(sex, half, shape) {
1586
+ const cls = "schematex-genogram-shape";
1587
+ switch (shape) {
1588
+ case "square":
1589
+ return rect({ x: -half, y: -half, width: half * 2, height: half * 2, class: cls });
1590
+ case "circle":
1591
+ return circle({ cx: 0, cy: 0, r: half, class: cls });
1592
+ case "diamond":
1593
+ return polygon({ points: `0,${-half} ${half},0 0,${half} ${-half},0`, class: cls });
1594
+ case "triangle":
1595
+ return polygon({ points: `0,${-half} ${half},${half} ${-half},${half}`, class: cls });
1596
+ case "triangle-down":
1597
+ return polygon({ points: `${-half},${-half} ${half},${-half} 0,${half}`, class: cls });
1598
+ }
1284
1599
  switch (sex) {
1285
1600
  case "male":
1286
- return rect({
1287
- x: -half,
1288
- y: -half,
1289
- width: half * 2,
1290
- height: half * 2,
1291
- class: "schematex-genogram-shape"
1292
- });
1601
+ return rect({ x: -half, y: -half, width: half * 2, height: half * 2, class: cls });
1293
1602
  case "female":
1294
- return circle({
1295
- cx: 0,
1296
- cy: 0,
1297
- r: half,
1298
- class: "schematex-genogram-shape"
1299
- });
1603
+ return circle({ cx: 0, cy: 0, r: half, class: cls });
1300
1604
  case "unknown":
1301
1605
  case "other":
1302
1606
  case "nonbinary":
1303
1607
  case "intersex":
1304
- return polygon({
1305
- points: `0,${-half} ${half},0 0,${half} ${-half},0`,
1306
- class: "schematex-genogram-shape"
1307
- });
1608
+ return polygon({ points: `0,${-half} ${half},0 0,${half} ${-half},0`, class: cls });
1308
1609
  }
1309
1610
  }
1310
1611
  function deceasedOverlay(sex, half) {
@@ -1401,6 +1702,7 @@ var STRUCTURAL_TYPES = /* @__PURE__ */ new Set([
1401
1702
  "separated",
1402
1703
  "engaged",
1403
1704
  "cohabiting",
1705
+ "cohabiting-ended",
1404
1706
  "domestic-partnership",
1405
1707
  "consanguineous",
1406
1708
  "parent-child",
@@ -1593,6 +1895,7 @@ function buildStructuralItems(rels, theme) {
1593
1895
  "separated",
1594
1896
  "engaged",
1595
1897
  "cohabiting",
1898
+ "cohabiting-ended",
1596
1899
  "domestic-partnership",
1597
1900
  "consanguineous",
1598
1901
  "adopted",
@@ -1622,6 +1925,14 @@ function structuralItem(t, theme) {
1622
1925
  return { ...base, kind: "edge", marker: "X", pattern: "solid" };
1623
1926
  case "separated":
1624
1927
  return { ...base, kind: "edge", marker: "slash", pattern: "solid" };
1928
+ case "cohabiting-ended":
1929
+ return {
1930
+ ...base,
1931
+ label: "Cohabiting (ended)",
1932
+ kind: "edge",
1933
+ marker: "slash",
1934
+ pattern: "dashed"
1935
+ };
1625
1936
  case "engaged":
1626
1937
  case "cohabiting":
1627
1938
  case "adopted":
@@ -1784,7 +2095,8 @@ function buildMarkerItems(individuals, theme) {
1784
2095
  "index-person",
1785
2096
  "transgender",
1786
2097
  "no-children",
1787
- "infertile"
2098
+ "infertile",
2099
+ "unknown-siblings"
1788
2100
  ];
1789
2101
  const items = [];
1790
2102
  for (const m of order) {
@@ -1817,6 +2129,16 @@ function markerItem(m, theme) {
1817
2129
  return { key: `marker.${m}`, label: "No children (by choice)", kind: "marker", marker: "slash", section: "markers" };
1818
2130
  case "infertile":
1819
2131
  return { key: `marker.${m}`, label: "Infertile", kind: "marker", marker: "X", section: "markers" };
2132
+ case "unknown-siblings":
2133
+ return {
2134
+ key: `marker.${m}`,
2135
+ label: "Sibling(s) \u2014 unknown count",
2136
+ kind: "shape",
2137
+ shape: "diamond",
2138
+ fill: "#ffffff",
2139
+ color: theme?.stroke,
2140
+ section: "markers"
2141
+ };
1820
2142
  default:
1821
2143
  return { key: `marker.${m}`, label: humanize(m), kind: "marker", marker: "dot", section: "markers" };
1822
2144
  }
@@ -1837,7 +2159,11 @@ function renderGenogram(layout, config, ast) {
1837
2159
  const emotionalLayer = renderEmotionalEdges(emotionalEdges);
1838
2160
  const nodeLayers = renderNodes(genGroups);
1839
2161
  const labelLayer = renderLabels(layout.nodes, config);
1840
- const edgeLabelLayer = renderEdgeLabels(structuralEdges, config);
2162
+ const labelEdges = structuralEdges.filter(
2163
+ (e) => !e.relationship.secondary
2164
+ );
2165
+ const edgeLabelLayer = renderEdgeLabels(labelEdges, config);
2166
+ const siblingOfLayer = renderSiblingOfBrackets(layout, ast);
1841
2167
  const nodeCount = layout.nodes.length;
1842
2168
  const genCount = genGroups.size;
1843
2169
  const chartTitle = ast?.metadata?.title;
@@ -1871,7 +2197,7 @@ function renderGenogram(layout, config, ast) {
1871
2197
  chartContent.push(
1872
2198
  group(
1873
2199
  { transform: titleHeight > 0 ? `translate(0, ${titleHeight})` : void 0 },
1874
- [edgeLayers, emotionalLayer, ...nodeLayers, labelLayer, edgeLabelLayer]
2200
+ [edgeLayers, siblingOfLayer, emotionalLayer, ...nodeLayers, labelLayer, edgeLabelLayer]
1875
2201
  )
1876
2202
  );
1877
2203
  let finalWidth = layout.width;
@@ -1933,8 +2259,15 @@ function buildStyles(config) {
1933
2259
  .schematex-genogram-label { font-family: ${config.fontFamily}; font-size: ${config.fontSize}px; text-anchor: middle; fill: ${t.text}; }
1934
2260
  .schematex-genogram-edge { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; fill: none; stroke-linecap: round; stroke-linejoin: round; }
1935
2261
  .schematex-genogram-edge-cohabiting path { stroke-dasharray: 6,4; }
2262
+ .schematex-genogram-edge-cohabiting-ended path { stroke-dasharray: 6,4; }
1936
2263
  .schematex-genogram-edge-divorced .schematex-genogram-divorce-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
1937
2264
  .schematex-genogram-edge-separated .schematex-genogram-separation-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
2265
+ .schematex-genogram-edge-cohabiting-ended .schematex-genogram-separation-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
2266
+ /* Secondary parent-child link (foster/adopted "current caregiver") \u2014 dotted, muted */
2267
+ .schematex-genogram-edge-secondary path { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 2,4; fill: none; opacity: 0.85; }
2268
+ /* Sibling-of bracket (known relative, unknown ancestry) \u2014 dashed */
2269
+ .schematex-genogram-sibling-of path { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 4,3; fill: none; opacity: 0.7; }
2270
+ .schematex-genogram-unknown-siblings-mark { fill: ${t.text}; pointer-events: none; }
1938
2271
  .schematex-genogram-deceased-mark { stroke: ${t.deceasedMark}; stroke-width: ${STROKE_WIDTH.normal}; stroke-linecap: round; }
1939
2272
  /* Inline fill on each .schematex-genogram-condition-fill element comes from cond.color. The CSS only sets a default for elements that did not receive an inline fill attribute. */
1940
2273
  .schematex-genogram-condition-fill:not([fill]) { fill: ${t.conditionFill}; }
@@ -2080,10 +2413,27 @@ function renderEdges(edges) {
2080
2413
  const children = [];
2081
2414
  for (const edge of edges) {
2082
2415
  const relType = edge.relationship.type;
2083
- const cssClass = `schematex-genogram-edge schematex-genogram-edge-${relType}`;
2416
+ const isSecondary = edge.relationship.secondary === true;
2417
+ const cssClass = isSecondary ? `schematex-genogram-edge schematex-genogram-edge-secondary schematex-genogram-edge-secondary-${relType}` : `schematex-genogram-edge schematex-genogram-edge-${relType}`;
2084
2418
  const elements = [
2085
2419
  el("path", { d: edge.path, class: "schematex-genogram-edge-path" })
2086
2420
  ];
2421
+ if (relType === "cohabiting-ended" && !isSecondary) {
2422
+ const mid = pathMidpoint(edge.path);
2423
+ if (mid) {
2424
+ elements.push(
2425
+ el("line", {
2426
+ x1: mid.x - 4,
2427
+ y1: mid.y - 6,
2428
+ x2: mid.x + 4,
2429
+ y2: mid.y + 6,
2430
+ class: "schematex-genogram-separation-mark",
2431
+ stroke: "#333",
2432
+ "stroke-width": "2"
2433
+ })
2434
+ );
2435
+ }
2436
+ }
2087
2437
  if (relType === "divorced") {
2088
2438
  const mid = pathMidpoint(edge.path);
2089
2439
  if (mid) {
@@ -2182,6 +2532,35 @@ function renderNodes(genGroups) {
2182
2532
  }
2183
2533
  return layers;
2184
2534
  }
2535
+ function renderSiblingOfBrackets(layout, ast) {
2536
+ if (!ast) return group({ class: "schematex-genogram-sibling-of-edges" }, []);
2537
+ const elements = [];
2538
+ const nodeById = new Map(layout.nodes.map((n) => [n.id, n]));
2539
+ for (const ind of ast.individuals) {
2540
+ if (!ind.siblingOf) continue;
2541
+ const fromNode = nodeById.get(ind.id);
2542
+ const toNode = nodeById.get(ind.siblingOf);
2543
+ if (!fromNode || !toNode) continue;
2544
+ if (fromNode.generation !== toNode.generation) continue;
2545
+ const fromCx = fromNode.x + fromNode.width / 2;
2546
+ const fromTopY = fromNode.y;
2547
+ const toCx = toNode.x + toNode.width / 2;
2548
+ const toTopY = toNode.y;
2549
+ const bracketY = Math.min(fromTopY, toTopY) - 12;
2550
+ const path2 = `M ${fromCx} ${fromTopY} L ${fromCx} ${bracketY} L ${toCx} ${bracketY} L ${toCx} ${toTopY}`;
2551
+ elements.push(
2552
+ group(
2553
+ {
2554
+ class: "schematex-genogram-sibling-of",
2555
+ "data-from": ind.id,
2556
+ "data-to": ind.siblingOf
2557
+ },
2558
+ [el("path", { d: path2 })]
2559
+ )
2560
+ );
2561
+ }
2562
+ return group({ class: "schematex-genogram-sibling-of-edges" }, elements);
2563
+ }
2185
2564
  function renderLabels(nodes, config) {
2186
2565
  const labels = [];
2187
2566
  for (const node of nodes) {
@@ -2238,5 +2617,5 @@ var genogram = {
2238
2617
  };
2239
2618
 
2240
2619
  export { ParseError, genogram, getRequiredDefs, layoutGenogram, parseGenogram, renderGenogram, renderIndividualSymbol };
2241
- //# sourceMappingURL=chunk-H2OEUBPO.js.map
2242
- //# sourceMappingURL=chunk-H2OEUBPO.js.map
2620
+ //# sourceMappingURL=chunk-SE23X5OE.js.map
2621
+ //# sourceMappingURL=chunk-SE23X5OE.js.map