schematex 0.9.3 → 0.9.4

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 (173) hide show
  1. package/README.md +21 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/ai/ai-sdk.cjs +23 -23
  4. package/dist/ai/ai-sdk.d.cts +3 -3
  5. package/dist/ai/ai-sdk.d.ts +3 -3
  6. package/dist/ai/ai-sdk.js +18 -18
  7. package/dist/ai/index.cjs +32 -32
  8. package/dist/ai/index.d.cts +4 -4
  9. package/dist/ai/index.d.ts +4 -4
  10. package/dist/ai/index.js +19 -19
  11. package/dist/{api-DXOhuK3e.d.ts → api-1xoGiseb.d.cts} +2 -2
  12. package/dist/{api-B7C7qGiG.d.cts → api-CgXRSnCn.d.ts} +2 -2
  13. package/dist/browser.cjs +24 -24
  14. package/dist/browser.d.cts +3 -3
  15. package/dist/browser.d.ts +3 -3
  16. package/dist/browser.js +18 -18
  17. package/dist/{chunk-C2BNO3CI.cjs → chunk-35NGXDT2.cjs} +12 -12
  18. package/dist/{chunk-C2BNO3CI.cjs.map → chunk-35NGXDT2.cjs.map} +1 -1
  19. package/dist/{chunk-ZUK4BY55.js → chunk-3J4DZPZC.js} +3 -3
  20. package/dist/{chunk-ZUK4BY55.js.map → chunk-3J4DZPZC.js.map} +1 -1
  21. package/dist/{chunk-JEQGWH5N.js → chunk-3PH2MQGN.js} +3 -3
  22. package/dist/{chunk-JEQGWH5N.js.map → chunk-3PH2MQGN.js.map} +1 -1
  23. package/dist/{chunk-ZQECHIBI.js → chunk-3WX24RCH.js} +3 -3
  24. package/dist/{chunk-ZQECHIBI.js.map → chunk-3WX24RCH.js.map} +1 -1
  25. package/dist/{chunk-ATE7LD6I.cjs → chunk-4AC6I7KJ.cjs} +4 -4
  26. package/dist/{chunk-ATE7LD6I.cjs.map → chunk-4AC6I7KJ.cjs.map} +1 -1
  27. package/dist/{chunk-D4QZ6UDO.js → chunk-4MRVJI7G.js} +110 -3
  28. package/dist/chunk-4MRVJI7G.js.map +1 -0
  29. package/dist/{chunk-VOFND6ZQ.cjs → chunk-4OC3CTGE.cjs} +4 -4
  30. package/dist/{chunk-VOFND6ZQ.cjs.map → chunk-4OC3CTGE.cjs.map} +1 -1
  31. package/dist/{chunk-VG5LP5A4.cjs → chunk-5ZQRHDMQ.cjs} +4 -4
  32. package/dist/{chunk-VG5LP5A4.cjs.map → chunk-5ZQRHDMQ.cjs.map} +1 -1
  33. package/dist/{chunk-6W6RFZND.cjs → chunk-627GHE2N.cjs} +5 -5
  34. package/dist/{chunk-6W6RFZND.cjs.map → chunk-627GHE2N.cjs.map} +1 -1
  35. package/dist/{chunk-TDSGQT7R.js → chunk-64LABNTF.js} +3 -3
  36. package/dist/{chunk-TDSGQT7R.js.map → chunk-64LABNTF.js.map} +1 -1
  37. package/dist/{chunk-AEZVCGH4.cjs → chunk-6QZQTASC.cjs} +12 -12
  38. package/dist/{chunk-AEZVCGH4.cjs.map → chunk-6QZQTASC.cjs.map} +1 -1
  39. package/dist/{chunk-FFAJQ36U.cjs → chunk-6ZD7TCWO.cjs} +15 -15
  40. package/dist/{chunk-FFAJQ36U.cjs.map → chunk-6ZD7TCWO.cjs.map} +1 -1
  41. package/dist/{chunk-PUD7PIY5.js → chunk-AXMBXAEA.js} +3 -3
  42. package/dist/{chunk-PUD7PIY5.js.map → chunk-AXMBXAEA.js.map} +1 -1
  43. package/dist/{chunk-X3GEGVW2.js → chunk-B4CMWA6Y.js} +3 -3
  44. package/dist/{chunk-X3GEGVW2.js.map → chunk-B4CMWA6Y.js.map} +1 -1
  45. package/dist/{chunk-O6A2GJLI.cjs → chunk-C4Y24X3U.cjs} +4 -4
  46. package/dist/{chunk-O6A2GJLI.cjs.map → chunk-C4Y24X3U.cjs.map} +1 -1
  47. package/dist/{chunk-CAAMBDEN.cjs → chunk-ENUM7GMZ.cjs} +110 -2
  48. package/dist/chunk-ENUM7GMZ.cjs.map +1 -0
  49. package/dist/{chunk-IOD2EFMX.cjs → chunk-GAQ36VFD.cjs} +4 -4
  50. package/dist/{chunk-IOD2EFMX.cjs.map → chunk-GAQ36VFD.cjs.map} +1 -1
  51. package/dist/{chunk-AHSSFGDJ.cjs → chunk-GTCAJPQR.cjs} +1738 -217
  52. package/dist/chunk-GTCAJPQR.cjs.map +1 -0
  53. package/dist/{chunk-6GHXO3WQ.js → chunk-GYYYULBL.js} +3 -3
  54. package/dist/{chunk-6GHXO3WQ.js.map → chunk-GYYYULBL.js.map} +1 -1
  55. package/dist/{chunk-IU26USXA.cjs → chunk-INVLJYAE.cjs} +4 -4
  56. package/dist/{chunk-IU26USXA.cjs.map → chunk-INVLJYAE.cjs.map} +1 -1
  57. package/dist/{chunk-BXS53MLV.cjs → chunk-ITI3STJ6.cjs} +4 -4
  58. package/dist/{chunk-BXS53MLV.cjs.map → chunk-ITI3STJ6.cjs.map} +1 -1
  59. package/dist/{chunk-XAYD5NVJ.js → chunk-JEMAOC2D.js} +3 -3
  60. package/dist/{chunk-XAYD5NVJ.js.map → chunk-JEMAOC2D.js.map} +1 -1
  61. package/dist/{chunk-PZCYFT2A.js → chunk-M26ORU4P.js} +3 -3
  62. package/dist/{chunk-PZCYFT2A.js.map → chunk-M26ORU4P.js.map} +1 -1
  63. package/dist/{chunk-JCJWSW5Y.js → chunk-NIYB6CHH.js} +326 -4
  64. package/dist/chunk-NIYB6CHH.js.map +1 -0
  65. package/dist/{chunk-U4AJLMHC.js → chunk-NKYR4PAS.js} +3 -3
  66. package/dist/{chunk-U4AJLMHC.js.map → chunk-NKYR4PAS.js.map} +1 -1
  67. package/dist/{chunk-TFNH2NLJ.cjs → chunk-OJ3P4IC4.cjs} +4 -4
  68. package/dist/{chunk-TFNH2NLJ.cjs.map → chunk-OJ3P4IC4.cjs.map} +1 -1
  69. package/dist/{chunk-7P4C5DMD.js → chunk-PFZKW3HE.js} +3 -3
  70. package/dist/chunk-PFZKW3HE.js.map +1 -0
  71. package/dist/{chunk-QHZEGWE7.js → chunk-RDYACU2G.js} +3 -3
  72. package/dist/{chunk-QHZEGWE7.js.map → chunk-RDYACU2G.js.map} +1 -1
  73. package/dist/{chunk-UNLLWCQK.js → chunk-SXOAAQNY.js} +3 -3
  74. package/dist/{chunk-UNLLWCQK.js.map → chunk-SXOAAQNY.js.map} +1 -1
  75. package/dist/{chunk-LFJE64RD.cjs → chunk-TX3YWZZX.cjs} +328 -6
  76. package/dist/chunk-TX3YWZZX.cjs.map +1 -0
  77. package/dist/{chunk-BP6MLXJU.js → chunk-UFXDAIDD.js} +1552 -31
  78. package/dist/chunk-UFXDAIDD.js.map +1 -0
  79. package/dist/{chunk-MZWVJFTV.cjs → chunk-VY6UZYYL.cjs} +15 -15
  80. package/dist/{chunk-MZWVJFTV.cjs.map → chunk-VY6UZYYL.cjs.map} +1 -1
  81. package/dist/{chunk-Y4OBXYGW.cjs → chunk-VYQXB2RC.cjs} +12 -12
  82. package/dist/{chunk-Y4OBXYGW.cjs.map → chunk-VYQXB2RC.cjs.map} +1 -1
  83. package/dist/{chunk-JYAL26WQ.cjs → chunk-WJXLF42K.cjs} +4 -4
  84. package/dist/chunk-WJXLF42K.cjs.map +1 -0
  85. package/dist/{chunk-OND4N5ZZ.js → chunk-XCCXG6RR.js} +3 -3
  86. package/dist/{chunk-OND4N5ZZ.js.map → chunk-XCCXG6RR.js.map} +1 -1
  87. package/dist/{chunk-4CLAD7VZ.js → chunk-ZCHGIWJK.js} +3 -3
  88. package/dist/{chunk-4CLAD7VZ.js.map → chunk-ZCHGIWJK.js.map} +1 -1
  89. package/dist/{diagnostics-fyjWjeDa.d.ts → diagnostics-CDwnQ65n.d.cts} +1 -1
  90. package/dist/{diagnostics-fyjWjeDa.d.cts → diagnostics-CDwnQ65n.d.ts} +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 +9 -9
  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 +7 -7
  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 +2 -2
  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 +9 -9
  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 +2 -2
  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 +8 -8
  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 +8 -8
  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 +7 -7
  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 +2 -2
  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 +8 -8
  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 +6 -6
  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 +2 -2
  147. package/dist/diagrams/timing/index.d.cts +1 -1
  148. package/dist/diagrams/timing/index.d.ts +1 -1
  149. package/dist/diagrams/venn/index.cjs +9 -9
  150. package/dist/diagrams/venn/index.d.cts +1 -1
  151. package/dist/diagrams/venn/index.d.ts +1 -1
  152. package/dist/diagrams/venn/index.js +2 -2
  153. package/dist/{index-CFaKjWPy.d.ts → index-CYSH3_ca.d.ts} +1 -1
  154. package/dist/{index-DZNmJWGg.d.cts → index-DNOoLmYX.d.cts} +1 -1
  155. package/dist/index.cjs +86 -86
  156. package/dist/index.d.cts +4 -4
  157. package/dist/index.d.ts +4 -4
  158. package/dist/index.js +22 -22
  159. package/dist/react.cjs +18 -18
  160. package/dist/react.d.cts +2 -2
  161. package/dist/react.d.ts +2 -2
  162. package/dist/react.js +17 -17
  163. package/dist/{tools-BDNih0Mb.d.ts → tools-BhTti-oG.d.ts} +3 -3
  164. package/dist/{tools-C23t_WeN.d.cts → tools-CbYmQ3hH.d.cts} +3 -3
  165. package/package.json +1 -1
  166. package/dist/chunk-7P4C5DMD.js.map +0 -1
  167. package/dist/chunk-AHSSFGDJ.cjs.map +0 -1
  168. package/dist/chunk-BP6MLXJU.js.map +0 -1
  169. package/dist/chunk-CAAMBDEN.cjs.map +0 -1
  170. package/dist/chunk-D4QZ6UDO.js.map +0 -1
  171. package/dist/chunk-JCJWSW5Y.js.map +0 -1
  172. package/dist/chunk-JYAL26WQ.cjs.map +0 -1
  173. package/dist/chunk-LFJE64RD.cjs.map +0 -1
@@ -1,21 +1,21 @@
1
- import { orgchart } from './chunk-ZUK4BY55.js';
2
- import { circuit } from './chunk-QHZEGWE7.js';
3
- import { blockdiagram } from './chunk-JEQGWH5N.js';
4
- import { ladder } from './chunk-X3GEGVW2.js';
5
- import { sld } from './chunk-7P4C5DMD.js';
6
- import { entity } from './chunk-XAYD5NVJ.js';
7
- import { fishbone } from './chunk-6GHXO3WQ.js';
8
- import { venn } from './chunk-UNLLWCQK.js';
9
- import { flowchart, layoutFlowchart } from './chunk-PUD7PIY5.js';
10
- import { genogram } from './chunk-PZCYFT2A.js';
11
- import { ecomap, estimateTextWidth } from './chunk-OND4N5ZZ.js';
12
- import { pedigree } from './chunk-4CLAD7VZ.js';
1
+ import { orgchart } from './chunk-3J4DZPZC.js';
2
+ import { circuit } from './chunk-RDYACU2G.js';
3
+ import { blockdiagram } from './chunk-3PH2MQGN.js';
4
+ import { ladder } from './chunk-B4CMWA6Y.js';
5
+ import { sld } from './chunk-PFZKW3HE.js';
6
+ import { entity } from './chunk-JEMAOC2D.js';
7
+ import { fishbone } from './chunk-GYYYULBL.js';
8
+ import { venn } from './chunk-SXOAAQNY.js';
9
+ import { flowchart, layoutFlowchart } from './chunk-AXMBXAEA.js';
10
+ import { genogram } from './chunk-M26ORU4P.js';
11
+ import { ecomap, estimateTextWidth } from './chunk-XCCXG6RR.js';
12
+ import { pedigree } from './chunk-ZCHGIWJK.js';
13
13
  import { parseFrontmatter } from './chunk-2KTQ75LN.js';
14
- import { phylo } from './chunk-ZQECHIBI.js';
15
- import { sociogram } from './chunk-TDSGQT7R.js';
14
+ import { phylo } from './chunk-3WX24RCH.js';
15
+ import { sociogram } from './chunk-64LABNTF.js';
16
16
  import { timing } from './chunk-MTIZIHWE.js';
17
- import { logic } from './chunk-U4AJLMHC.js';
18
- import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, TITLE } from './chunk-D4QZ6UDO.js';
17
+ import { logic } from './chunk-NKYR4PAS.js';
18
+ import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, TITLE, resolvePlaybookTheme } from './chunk-4MRVJI7G.js';
19
19
  import { QUOTE_PAIRS, stripQuotes, isOpenQuote, extractQuotedString, matchQuotedTitle } from './chunk-TO6PNBT3.js';
20
20
  import { el, escapeXml, group, rect, text, line, path, circle, polygon, title, desc, svgRoot, defs, multilineText } from './chunk-SYYBKDL7.js';
21
21
 
@@ -1876,7 +1876,7 @@ function parseTimeline(src) {
1876
1876
  let i = 0;
1877
1877
  let autoId = 0;
1878
1878
  const nextId2 = (prefix) => `${prefix}-${++autoId}`;
1879
- const ordinal = { index: 0 };
1879
+ const ordinal2 = { index: 0 };
1880
1880
  const first = lines[0];
1881
1881
  if (/^timeline\b/i.test(first.text)) {
1882
1882
  const rest = first.text.replace(/^timeline\b/i, "").trim();
@@ -1947,7 +1947,7 @@ function parseTimeline(src) {
1947
1947
  i++;
1948
1948
  continue;
1949
1949
  }
1950
- const parsed2 = parseEventLine(child.text, child.line, nextId2, ordinal);
1950
+ const parsed2 = parseEventLine(child.text, child.line, nextId2, ordinal2);
1951
1951
  if (!parsed2) throw new TimelineParseError(`Unrecognized line in ${keyword}: ${child.text}`, child.line);
1952
1952
  parsed2.event.trackId = trackId;
1953
1953
  ast.events.push(parsed2.event);
@@ -1961,7 +1961,7 @@ function parseTimeline(src) {
1961
1961
  }
1962
1962
  continue;
1963
1963
  }
1964
- const parsed = parseEventLine(text2, L.line, nextId2, ordinal);
1964
+ const parsed = parseEventLine(text2, L.line, nextId2, ordinal2);
1965
1965
  if (parsed) {
1966
1966
  ast.events.push(parsed.event);
1967
1967
  i++;
@@ -1985,12 +1985,12 @@ function safeParseDate(raw, line2) {
1985
1985
  throw new TimelineParseError(msg, line2);
1986
1986
  }
1987
1987
  }
1988
- function parseRowKey(raw, ordinal) {
1988
+ function parseRowKey(raw, ordinal2) {
1989
1989
  const parsed = tryParseDate(raw);
1990
1990
  if (parsed) return parsed;
1991
- ordinal.index += 1;
1991
+ ordinal2.index += 1;
1992
1992
  return {
1993
- value: ordinal.index,
1993
+ value: ordinal2.index,
1994
1994
  raw,
1995
1995
  precision: "ordinal"
1996
1996
  };
@@ -2027,7 +2027,7 @@ function applyConfig(ast, k, v, line2) {
2027
2027
  (ast.metadata ??= {})[k] = v;
2028
2028
  }
2029
2029
  }
2030
- function parseEventLine(text2, line2, nextId2, ordinal) {
2030
+ function parseEventLine(text2, line2, nextId2, ordinal2) {
2031
2031
  const { props, rest } = parseProperties(text2, line2);
2032
2032
  const { date, end, body } = splitDateAndBody(rest, line2);
2033
2033
  let kind = end ? "range" : "point";
@@ -2044,8 +2044,8 @@ function parseEventLine(text2, line2, nextId2, ordinal) {
2044
2044
  id: nextId2("ev"),
2045
2045
  label,
2046
2046
  kind,
2047
- start: parseRowKey(date, ordinal),
2048
- end: end ? parseRowKey(end, ordinal) : void 0,
2047
+ start: parseRowKey(date, ordinal2),
2048
+ end: end ? parseRowKey(end, ordinal2) : void 0,
2049
2049
  icon: props["icon"],
2050
2050
  shape: props["shape"],
2051
2051
  color: props["color"],
@@ -38071,7 +38071,7 @@ function renderErrorPanel(lay, t) {
38071
38071
  const w = Math.max(560, ...lines.map((l) => l.length * 6.6 + 48));
38072
38072
  const h = 56 + lines.length * 19;
38073
38073
  return svgRoot(
38074
- { viewBox: `0 0 ${r24(w)} ${h}`, class: "sx-fp", role: "img" },
38074
+ { viewBox: `0 0 ${r24(w)} ${h}`, width: r24(w), height: h, class: "sx-fp", role: "img" },
38075
38075
  [
38076
38076
  title(lay.title),
38077
38077
  desc(`Floor plan validation failed with ${lines.length} error${lines.length === 1 ? "" : "s"}.`),
@@ -38407,7 +38407,7 @@ function renderFloorplanLayout(lay, config) {
38407
38407
  const nRooms = lay.rooms.length;
38408
38408
  const descText = `${nRooms} room${nRooms === 1 ? "" : "s"}, ${formatArea(lay.totalAreaM2, lay.unit)} total. ${lay.items.length} furniture item${lay.items.length === 1 ? "" : "s"}.` + (lay.warnings.length ? ` Warnings: ${lay.warnings.join("; ")}.` : "");
38409
38409
  return svgRoot(
38410
- { viewBox: `0 0 ${W2} ${H2}`, class: "sx-fp", role: "img" },
38410
+ { viewBox: `0 0 ${W2} ${H2}`, width: W2, height: H2, class: "sx-fp", role: "img" },
38411
38411
  [
38412
38412
  title(lay.title),
38413
38413
  desc(descText),
@@ -38473,6 +38473,1526 @@ var floorplan = {
38473
38473
  }
38474
38474
  };
38475
38475
 
38476
+ // src/diagrams/playbook/parser.ts
38477
+ var PlaybookParseError = class extends Error {
38478
+ line;
38479
+ constructor(message, line2) {
38480
+ super(`line ${line2}: ${message}`);
38481
+ this.name = "PlaybookParseError";
38482
+ this.line = line2;
38483
+ }
38484
+ };
38485
+ var isStr2 = (t) => t !== void 0 && "str" in t;
38486
+ var isWord2 = (t, w) => t !== void 0 && "word" in t && (w === void 0 || t.word.toLowerCase() === w);
38487
+ var tokDisplay = (t) => "word" in t ? t.word : `"${t.str}"`;
38488
+ var normalizeQuotes4 = (s) => s.replace(/[“”「」『』]/g, '"').replace(/[‘’]/g, "'");
38489
+ function tokenize8(line2) {
38490
+ const out = [];
38491
+ const re = /"([^"]*)"|(\S+)/g;
38492
+ let m;
38493
+ while (m = re.exec(line2)) {
38494
+ if (m[1] !== void 0) out.push({ str: m[1] });
38495
+ else out.push({ word: m[2] });
38496
+ }
38497
+ return out;
38498
+ }
38499
+ function parseNum2(t, what, ln) {
38500
+ if (!isWord2(t)) throw new PlaybookParseError(`expected a number for ${what}`, ln);
38501
+ const v = Number(t.word);
38502
+ if (!Number.isFinite(v)) throw new PlaybookParseError(`expected a number for ${what}, got "${t.word}"`, ln);
38503
+ return v;
38504
+ }
38505
+ function parseId2(t, what, ln) {
38506
+ if (!isWord2(t)) throw new PlaybookParseError(`expected ${what}`, ln);
38507
+ return t.word;
38508
+ }
38509
+ var COORD = /^([+-]?\d*\.?\d+),([+-]?\d*\.?\d+)$/;
38510
+ var POSITIONS = ["o", "c", "ol", "qb", "rb", "wr", "te", "x", "dl", "lb", "db", "s", "gk"];
38511
+ var NAMED_ROUTES = [
38512
+ "go",
38513
+ "fly",
38514
+ "streak",
38515
+ "vertical",
38516
+ "slant",
38517
+ "flat",
38518
+ "hitch",
38519
+ "out",
38520
+ "in",
38521
+ "dig",
38522
+ "curl",
38523
+ "comeback",
38524
+ "corner",
38525
+ "flag",
38526
+ "post",
38527
+ "wheel",
38528
+ "cross",
38529
+ "drag",
38530
+ "seam",
38531
+ "screen",
38532
+ "dive",
38533
+ "iso",
38534
+ "power",
38535
+ "counter",
38536
+ "sweep",
38537
+ "toss",
38538
+ "draw",
38539
+ "trap"
38540
+ ];
38541
+ var RUN_CONCEPTS = /* @__PURE__ */ new Set(["dive", "iso", "power", "counter", "sweep", "toss", "draw", "trap"]);
38542
+ var FORMATIONS = [
38543
+ "i-form",
38544
+ "shotgun",
38545
+ "singleback",
38546
+ "pistol",
38547
+ "trips",
38548
+ "spread",
38549
+ "trips-right",
38550
+ "trips-left",
38551
+ "empty",
38552
+ "goal-line",
38553
+ "wishbone",
38554
+ "4-4-2",
38555
+ "4-3-3",
38556
+ "4-2-3-1",
38557
+ "3-5-2",
38558
+ "4-4-1-1",
38559
+ "4-5-1",
38560
+ "3-4-3",
38561
+ "horns",
38562
+ "1-4-high",
38563
+ "1-4-low",
38564
+ "box",
38565
+ "spread-pnr",
38566
+ "5-out",
38567
+ "4-out"
38568
+ ];
38569
+ var DEFENSES = [
38570
+ "4-3",
38571
+ "3-4",
38572
+ "4-4",
38573
+ "nickel",
38574
+ "dime",
38575
+ "cover-0",
38576
+ "cover-1",
38577
+ "cover-2",
38578
+ "cover-3",
38579
+ "cover-4",
38580
+ "cover-6",
38581
+ "man",
38582
+ "zone-2-3",
38583
+ "zone-3-2",
38584
+ "zone-1-3-1",
38585
+ "low-block",
38586
+ "mid-block",
38587
+ "high-press"
38588
+ ];
38589
+ var DIRS = ["left", "right"];
38590
+ function parseHeader4(tok, ast, ln) {
38591
+ while (tok.length) {
38592
+ const t = tok.shift();
38593
+ if (isStr2(t)) ast.title = t.str;
38594
+ else if (isWord2(t, "sport")) {
38595
+ const s = parseId2(tok.shift(), "sport", ln).toLowerCase();
38596
+ if (s !== "football" && s !== "basketball" && s !== "soccer") {
38597
+ throw new PlaybookParseError(`sport must be football|basketball|soccer, got "${s}"`, ln);
38598
+ }
38599
+ ast.sport = s;
38600
+ } else throw new PlaybookParseError(`playbook: unexpected token "${tokDisplay(t)}"`, ln);
38601
+ }
38602
+ }
38603
+ function parseField(tok, ast, ln) {
38604
+ while (tok.length) {
38605
+ const t = tok.shift();
38606
+ if (isWord2(t, "down")) ast.down = parseNum2(tok.shift(), "down", ln);
38607
+ else if (isWord2(t, "distance") || isWord2(t, "togo")) ast.distance = parseNum2(tok.shift(), "distance", ln);
38608
+ else if (isWord2(t, "los") || isWord2(t, "ball")) ast.losYard = parseNum2(tok.shift(), "los", ln);
38609
+ else if (isWord2(t, "goal") || isWord2(t, "togoal")) ast.toGoal = parseNum2(tok.shift(), "goal", ln);
38610
+ else if (isWord2(t, "view")) {
38611
+ const v = parseId2(tok.shift(), "view (full|half)", ln).toLowerCase();
38612
+ if (v !== "full" && v !== "half" && v !== "auto") throw new PlaybookParseError(`view must be full|half`, ln);
38613
+ ast.view = v;
38614
+ } else if (isWord2(t, "hash")) {
38615
+ const h = parseId2(tok.shift(), "hash (nfl|college|none)", ln).toLowerCase();
38616
+ if (h !== "nfl" && h !== "college" && h !== "none") throw new PlaybookParseError(`hash must be nfl|college|none`, ln);
38617
+ ast.hash = h;
38618
+ } else throw new PlaybookParseError(`field: unexpected token "${tokDisplay(t)}"`, ln);
38619
+ }
38620
+ }
38621
+ function parseFormation(tok, ast, ln) {
38622
+ let name = parseId2(tok.shift(), "a formation", ln).toLowerCase();
38623
+ let side;
38624
+ const hy = /^(.*)-(left|right)$/.exec(name);
38625
+ if (hy && FORMATIONS.includes(hy[1]) && hy[1] !== "trips") {
38626
+ name = hy[1];
38627
+ side = hy[2];
38628
+ }
38629
+ if (!FORMATIONS.includes(name)) {
38630
+ throw new PlaybookParseError(`unknown formation "${name}"`, ln);
38631
+ }
38632
+ ast.formation = name;
38633
+ while (tok.length) {
38634
+ const t = tok.shift();
38635
+ if (isWord2(t, "left") || isWord2(t, "right")) side = t.word.toLowerCase();
38636
+ else throw new PlaybookParseError(`formation: unexpected token "${tokDisplay(t)}"`, ln);
38637
+ }
38638
+ if (name === "trips-right") ast.formationSide = "right";
38639
+ else if (name === "trips-left") ast.formationSide = "left";
38640
+ else if (side) ast.formationSide = side;
38641
+ }
38642
+ function parseDefense(tok, ast, ln) {
38643
+ const d = parseId2(tok.shift(), "a defensive scheme", ln).toLowerCase();
38644
+ if (!DEFENSES.includes(d)) throw new PlaybookParseError(`unknown defense "${d}"`, ln);
38645
+ ast.defense = d;
38646
+ }
38647
+ function parsePlayer(tok, ast, ln) {
38648
+ const id = parseId2(tok.shift(), "a player id", ln);
38649
+ const posRaw = parseId2(tok.shift(), "a position", ln).toLowerCase();
38650
+ if (!POSITIONS.includes(posRaw)) throw new PlaybookParseError(`unknown position "${posRaw}"`, ln);
38651
+ const isDef = posRaw === "x" || posRaw === "dl" || posRaw === "lb" || posRaw === "db" || posRaw === "s";
38652
+ const p = { id, side: isDef ? "defense" : "offense", pos: posRaw, label: id, line: ln };
38653
+ while (tok.length) {
38654
+ const t = tok.shift();
38655
+ if (isStr2(t)) p.label = t.str;
38656
+ else if (isWord2(t, "label")) {
38657
+ const lt = tok.shift();
38658
+ p.label = isStr2(lt) ? lt.str : parseId2(lt, "a label", ln);
38659
+ } else if (isWord2(t, "at")) {
38660
+ const c = COORD.exec(parseId2(tok.shift(), "x,y", ln));
38661
+ if (!c) throw new PlaybookParseError(`expected "x,y" after at`, ln);
38662
+ p.at = { x: Number(c[1]), y: Number(c[2]) };
38663
+ } else if (isWord2(t, "side")) {
38664
+ const sv = parseId2(tok.shift(), "side", ln).toLowerCase();
38665
+ if (sv !== "offense" && sv !== "defense") throw new PlaybookParseError(`side must be offense|defense`, ln);
38666
+ p.side = sv;
38667
+ } else throw new PlaybookParseError(`player: unexpected token "${tokDisplay(t)}"`, ln);
38668
+ }
38669
+ ast.players.push(p);
38670
+ }
38671
+ function splitNameDir(token) {
38672
+ const m = /^(.*)-(left|right)$/.exec(token.toLowerCase());
38673
+ if (m) return { name: m[1], dir: m[2] };
38674
+ return { name: token.toLowerCase() };
38675
+ }
38676
+ function parseDests(tok) {
38677
+ const pts = [];
38678
+ while (tok.length) {
38679
+ const t = tok.shift();
38680
+ if (isWord2(t, "to") || isWord2(t, "then") || isWord2(t, "->")) continue;
38681
+ if (!isWord2(t)) continue;
38682
+ const c = COORD.exec(t.word);
38683
+ if (c) {
38684
+ const rel = /^[+]/.test(c[1]) || /^[+]/.test(c[2]);
38685
+ pts.push({ x: Number(c[1]), y: Number(c[2]), rel });
38686
+ } else {
38687
+ pts.push({ ref: t.word });
38688
+ }
38689
+ }
38690
+ return pts;
38691
+ }
38692
+ function parseRouteRun(kind, tok, ast, ln) {
38693
+ const player = parseId2(tok.shift(), "a player id", ln);
38694
+ const m = { player, kind, line: ln };
38695
+ const first = tok[0];
38696
+ if (isWord2(first, "to") || isWord2(first, "path")) {
38697
+ tok.shift();
38698
+ m.points = parseDests(tok);
38699
+ ast.moves.push(m);
38700
+ return;
38701
+ }
38702
+ if (isWord2(first)) {
38703
+ const { name, dir } = splitNameDir(first.word);
38704
+ if (NAMED_ROUTES.includes(name)) {
38705
+ tok.shift();
38706
+ m.named = name;
38707
+ if (dir) m.dir = dir;
38708
+ if (kind === "route" && RUN_CONCEPTS.has(name)) m.kind = "run";
38709
+ while (tok.length) {
38710
+ const t = tok.shift();
38711
+ if (isWord2(t) && DIRS.includes(t.word.toLowerCase())) m.dir = t.word.toLowerCase();
38712
+ else if (isWord2(t) && Number.isFinite(Number(t.word))) m.depth = Number(t.word);
38713
+ else if (isWord2(t, "depth")) m.depth = parseNum2(tok.shift(), "depth", ln);
38714
+ else throw new PlaybookParseError(`${kind}: unexpected token "${tokDisplay(t)}"`, ln);
38715
+ }
38716
+ ast.moves.push(m);
38717
+ return;
38718
+ }
38719
+ }
38720
+ m.points = parseDests(tok);
38721
+ if (m.points.length === 0) throw new PlaybookParseError(`${kind} ${player}: needs a route name or destination`, ln);
38722
+ ast.moves.push(m);
38723
+ }
38724
+ function parsePoly(kind, tok, ast, ln) {
38725
+ const player = parseId2(tok.shift(), "a player id", ln);
38726
+ const points = parseDests(tok);
38727
+ if (points.length === 0) throw new PlaybookParseError(`${kind} ${player}: needs a destination (x,y or a landmark)`, ln);
38728
+ ast.moves.push({ player, kind, points, line: ln });
38729
+ }
38730
+ function parseTargeted(kind, tok, ast, ln, optionalTarget = false) {
38731
+ const player = parseId2(tok.shift(), "a player id", ln);
38732
+ const nxt = tok[0];
38733
+ if (isWord2(nxt, "to")) tok.shift();
38734
+ const tgt = tok.shift();
38735
+ if (!tgt) {
38736
+ if (optionalTarget) {
38737
+ ast.moves.push({ player, kind, line: ln });
38738
+ return;
38739
+ }
38740
+ throw new PlaybookParseError(`${kind} ${player}: needs a target (player id, landmark, or x,y)`, ln);
38741
+ }
38742
+ const target = isWord2(tgt) ? tgt.word : tgt.str;
38743
+ ast.moves.push({ player, kind, target, line: ln });
38744
+ }
38745
+ function parsePull(tok, ast, ln) {
38746
+ const player = parseId2(tok.shift(), "a lineman id", ln);
38747
+ const dt = tok.shift();
38748
+ const { dir } = splitNameDir(isWord2(dt) ? dt.word : "");
38749
+ let d = dir;
38750
+ if (!d && isWord2(dt) && DIRS.includes(dt.word.toLowerCase())) d = dt.word.toLowerCase();
38751
+ if (!d) throw new PlaybookParseError(`pull: expected left|right`, ln);
38752
+ ast.moves.push({ player, kind: "pull", named: "power", dir: d, line: ln });
38753
+ }
38754
+ function parseMotion(tok, ast, ln) {
38755
+ const player = parseId2(tok.shift(), "a player id", ln);
38756
+ const m = { player, kind: "motion", line: ln };
38757
+ const dt = tok.shift();
38758
+ if (isWord2(dt) && DIRS.includes(dt.word.toLowerCase())) {
38759
+ m.dir = dt.word.toLowerCase();
38760
+ const n = tok.shift();
38761
+ if (isWord2(n) && Number.isFinite(Number(n.word))) m.depth = Number(n.word);
38762
+ } else if (isWord2(dt, "to")) {
38763
+ m.points = parseDests(tok);
38764
+ } else if (isWord2(dt)) {
38765
+ const c = COORD.exec(dt.word);
38766
+ if (c) m.points = [{ x: Number(c[1]), y: Number(c[2]), rel: /^[+]/.test(c[1]) }];
38767
+ else m.points = [{ ref: dt.word }];
38768
+ } else throw new PlaybookParseError(`motion: expected left|right or a destination`, ln);
38769
+ ast.moves.push(m);
38770
+ }
38771
+ function parseZone(tok, ast, ln) {
38772
+ const z = { x: 0, y: 0, rx: 6, ry: 5, line: ln };
38773
+ while (tok.length) {
38774
+ const t = tok.shift();
38775
+ if (isStr2(t)) z.label = t.str;
38776
+ else if (isWord2(t, "at")) {
38777
+ const c = COORD.exec(parseId2(tok.shift(), "x,y", ln));
38778
+ if (!c) throw new PlaybookParseError(`zone: expected "x,y" after at`, ln);
38779
+ z.x = Number(c[1]);
38780
+ z.y = Number(c[2]);
38781
+ } else if (isWord2(t, "size")) {
38782
+ const st = tok.shift();
38783
+ const m = /^(\d*\.?\d+)x(\d*\.?\d+)$/i.exec(isWord2(st) ? st.word : "");
38784
+ if (!m) throw new PlaybookParseError(`zone size expects "rxXry"`, ln);
38785
+ z.rx = Number(m[1]);
38786
+ z.ry = Number(m[2]);
38787
+ } else if (isWord2(t, "label")) {
38788
+ const lt = tok.shift();
38789
+ z.label = isStr2(lt) ? lt.str : parseId2(lt, "a label", ln);
38790
+ } else throw new PlaybookParseError(`zone: unexpected token "${tokDisplay(t)}"`, ln);
38791
+ }
38792
+ ast.zones.push(z);
38793
+ }
38794
+ function parseHandoff(tok, ast, ln) {
38795
+ const from = parseId2(tok.shift(), "the source id", ln);
38796
+ const to = parseId2(tok.shift(), "the target id", ln);
38797
+ ast.moves.push({ player: from, kind: "handoff", target: to, line: ln });
38798
+ }
38799
+ function parsePlaybook(text2) {
38800
+ const ast = {
38801
+ type: "playbook",
38802
+ title: "Football Play",
38803
+ sport: "football",
38804
+ down: 0,
38805
+ distance: 0,
38806
+ hash: "nfl",
38807
+ view: "auto",
38808
+ formationSide: "right",
38809
+ players: [],
38810
+ moves: [],
38811
+ zones: []
38812
+ };
38813
+ let sawHeader = false;
38814
+ let titleSetByUser = false;
38815
+ const lines = text2.split(/\r?\n/);
38816
+ for (let i = 0; i < lines.length; i++) {
38817
+ const ln = i + 1;
38818
+ const raw = normalizeQuotes4(lines[i]).trim();
38819
+ if (!raw) continue;
38820
+ const all = tokenize8(raw);
38821
+ const tok = [];
38822
+ for (const t of all) {
38823
+ if (isWord2(t) && (t.word.startsWith("#") || t.word.startsWith("//"))) break;
38824
+ tok.push(t);
38825
+ }
38826
+ if (tok.length === 0) continue;
38827
+ const head = tok.shift();
38828
+ if (!isWord2(head)) throw new PlaybookParseError(`unexpected string at line start`, ln);
38829
+ const kw = head.word.toLowerCase();
38830
+ if (kw === "playbook") {
38831
+ const before = ast.title;
38832
+ parseHeader4(tok, ast, ln);
38833
+ if (ast.title !== before) titleSetByUser = true;
38834
+ sawHeader = true;
38835
+ continue;
38836
+ }
38837
+ if (!sawHeader) throw new PlaybookParseError(`the first statement must be the "playbook" header`, ln);
38838
+ if (kw === "field") parseField(tok, ast, ln);
38839
+ else if (kw === "view") {
38840
+ const v = parseId2(tok.shift(), "view (full|half)", ln).toLowerCase();
38841
+ if (v !== "full" && v !== "half" && v !== "auto") throw new PlaybookParseError(`view must be full|half`, ln);
38842
+ ast.view = v;
38843
+ } else if (kw === "formation" || kw === "set") parseFormation(tok, ast, ln);
38844
+ else if (kw === "defense" || kw === "def") parseDefense(tok, ast, ln);
38845
+ else if (kw === "player") parsePlayer(tok, ast, ln);
38846
+ else if (kw === "route") parseRouteRun("route", tok, ast, ln);
38847
+ else if (kw === "run") parseRouteRun("run", tok, ast, ln);
38848
+ else if (kw === "cut") parsePoly("cut", tok, ast, ln);
38849
+ else if (kw === "move") parsePoly("move", tok, ast, ln);
38850
+ else if (kw === "dribble" || kw === "drive") parsePoly("dribble", tok, ast, ln);
38851
+ else if (kw === "pass") parseTargeted("pass", tok, ast, ln);
38852
+ else if (kw === "screen") parseTargeted("screen", tok, ast, ln);
38853
+ else if (kw === "block") parseTargeted("block", tok, ast, ln);
38854
+ else if (kw === "shot" || kw === "shoot") parseTargeted("shot", tok, ast, ln, true);
38855
+ else if (kw === "pull") parsePull(tok, ast, ln);
38856
+ else if (kw === "handoff") parseHandoff(tok, ast, ln);
38857
+ else if (kw === "motion") parseMotion(tok, ast, ln);
38858
+ else if (kw === "zone") parseZone(tok, ast, ln);
38859
+ else throw new PlaybookParseError(`unknown keyword "${kw}"`, ln);
38860
+ }
38861
+ if (!titleSetByUser) {
38862
+ ast.title = ast.sport === "basketball" ? "Basketball Play" : ast.sport === "soccer" ? "Soccer Tactic" : "Football Play";
38863
+ }
38864
+ return ast;
38865
+ }
38866
+
38867
+ // src/diagrams/playbook/geometry/football.ts
38868
+ var K = {
38869
+ scale: 14,
38870
+ olSplit: 1.4,
38871
+ wrSplit: 13,
38872
+ slotSplit: 6,
38873
+ minHalfWidth: 20,
38874
+ margin: 2,
38875
+ hashNfl: 3.08,
38876
+ hashCollege: 6.67
38877
+ };
38878
+ var r25 = (n) => Math.round(n * 100) / 100;
38879
+ var round22 = (p) => ({ x: Math.round(p.x * 100) / 100, y: Math.round(p.y * 100) / 100 });
38880
+ var dirSign = (d) => d === "right" ? 1 : -1;
38881
+ function offensiveLine() {
38882
+ return [
38883
+ { id: "LT", pos: "ol", label: "T", x: -2 * K.olSplit, y: 0 },
38884
+ { id: "LG", pos: "ol", label: "G", x: -1 * K.olSplit, y: 0 },
38885
+ { id: "C", pos: "c", label: "C", x: 0, y: 0 },
38886
+ { id: "RG", pos: "ol", label: "G", x: 1 * K.olSplit, y: 0 },
38887
+ { id: "RT", pos: "ol", label: "T", x: 2 * K.olSplit, y: 0 }
38888
+ ];
38889
+ }
38890
+ function tightEnd(side) {
38891
+ const s = side === "right" ? 1 : -1;
38892
+ return { id: "Y", pos: "te", label: "Y", x: s * (2 * K.olSplit + 1.3), y: 0 };
38893
+ }
38894
+ function formationRoster(formation, side) {
38895
+ const s = side === "right" ? 1 : -1;
38896
+ const ol = offensiveLine();
38897
+ const X = { id: "X", pos: "wr", label: "X", x: -s * K.wrSplit, y: 0 };
38898
+ const Z = { id: "Z", pos: "wr", label: "Z", x: s * K.wrSplit, y: -1 };
38899
+ switch (formation) {
38900
+ case "i-form":
38901
+ return [
38902
+ ...ol,
38903
+ tightEnd(side),
38904
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
38905
+ { id: "FB", pos: "rb", label: "F", x: 0, y: -3.6 },
38906
+ { id: "RB", pos: "rb", label: "RB", x: 0, y: -6.2 },
38907
+ X,
38908
+ Z
38909
+ ];
38910
+ case "wishbone":
38911
+ return [
38912
+ ...ol,
38913
+ tightEnd(side),
38914
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
38915
+ { id: "FB", pos: "rb", label: "F", x: 0, y: -3.4 },
38916
+ { id: "LH", pos: "rb", label: "H", x: -3.5, y: -5.2 },
38917
+ { id: "RB", pos: "rb", label: "H", x: 3.5, y: -5.2 },
38918
+ X
38919
+ ];
38920
+ case "singleback":
38921
+ return [
38922
+ ...ol,
38923
+ tightEnd(side),
38924
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
38925
+ { id: "RB", pos: "rb", label: "RB", x: 0, y: -5 },
38926
+ X,
38927
+ { id: "H", pos: "wr", label: "H", x: s * K.slotSplit, y: -1 },
38928
+ Z
38929
+ ];
38930
+ case "pistol":
38931
+ return [
38932
+ ...ol,
38933
+ tightEnd(side),
38934
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -4 },
38935
+ { id: "RB", pos: "rb", label: "RB", x: 0, y: -7 },
38936
+ X,
38937
+ Z
38938
+ ];
38939
+ case "shotgun":
38940
+ return [
38941
+ ...ol,
38942
+ tightEnd(side),
38943
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
38944
+ { id: "RB", pos: "rb", label: "RB", x: s * 1.8, y: -5 },
38945
+ X,
38946
+ Z
38947
+ ];
38948
+ case "spread":
38949
+ return [
38950
+ ...ol,
38951
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
38952
+ { id: "RB", pos: "rb", label: "RB", x: s * 1.8, y: -5 },
38953
+ { id: "X", pos: "wr", label: "X", x: -13 - 1, y: 0 },
38954
+ { id: "H", pos: "wr", label: "H", x: -6, y: -1 },
38955
+ { id: "Y", pos: "wr", label: "Y", x: K.slotSplit, y: -1 },
38956
+ { id: "Z", pos: "wr", label: "Z", x: K.wrSplit + 1, y: 0 }
38957
+ ];
38958
+ case "trips":
38959
+ case "trips-right":
38960
+ case "trips-left": {
38961
+ const t = formation === "trips-left" ? -1 : formation === "trips-right" ? 1 : s;
38962
+ return [
38963
+ ...ol,
38964
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
38965
+ { id: "RB", pos: "rb", label: "RB", x: -t * 1.8, y: -5 },
38966
+ { id: "X", pos: "wr", label: "X", x: -t * (K.wrSplit + 1), y: 0 },
38967
+ { id: "Z", pos: "wr", label: "Z", x: t * (K.wrSplit + 1), y: 0 },
38968
+ { id: "H", pos: "wr", label: "H", x: t * 9, y: -1 },
38969
+ { id: "Y", pos: "wr", label: "Y", x: t * 5, y: -1 }
38970
+ ];
38971
+ }
38972
+ case "empty":
38973
+ return [
38974
+ ...ol,
38975
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
38976
+ { id: "X", pos: "wr", label: "X", x: -13 - 1, y: 0 },
38977
+ { id: "H", pos: "wr", label: "H", x: -6 - 1, y: -1 },
38978
+ { id: "Z", pos: "wr", label: "Z", x: K.wrSplit + 1, y: 0 },
38979
+ { id: "Y", pos: "wr", label: "Y", x: K.slotSplit + 1, y: -1 },
38980
+ { id: "H2", pos: "wr", label: "h", x: K.slotSplit - 2, y: -1 }
38981
+ ];
38982
+ case "goal-line":
38983
+ return [
38984
+ ...ol,
38985
+ { id: "Y", pos: "te", label: "Y", x: 2 * K.olSplit + 1.3, y: 0 },
38986
+ { id: "TE2", pos: "te", label: "Y", x: -4.1, y: 0 },
38987
+ { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
38988
+ { id: "FB", pos: "rb", label: "F", x: 0, y: -3.4 },
38989
+ { id: "RB", pos: "rb", label: "RB", x: 0, y: -5.4 },
38990
+ { id: "Z", pos: "wr", label: "Z", x: s * 9, y: 0 }
38991
+ ];
38992
+ default:
38993
+ return [...ol, { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 }];
38994
+ }
38995
+ }
38996
+ function front43(side) {
38997
+ const s = side === "right" ? 1 : -1;
38998
+ return [
38999
+ { id: "DE_W", pos: "dl", label: "E", x: -3.6, y: 1 },
39000
+ { id: "DT_W", pos: "dl", label: "T", x: -1.2, y: 1 },
39001
+ { id: "DT_S", pos: "dl", label: "T", x: 1.2, y: 1 },
39002
+ { id: "DE_S", pos: "dl", label: "E", x: 3.6, y: 1 },
39003
+ { id: "WLB", pos: "lb", label: "W", x: -4.5, y: 4.3 },
39004
+ { id: "MLB", pos: "lb", label: "M", x: 0, y: 4.8 },
39005
+ { id: "SLB", pos: "lb", label: "S", x: s * 4.5, y: 4.3 }
39006
+ ];
39007
+ }
39008
+ function front34(side) {
39009
+ const s = side === "right" ? 1 : -1;
39010
+ return [
39011
+ { id: "DE_W", pos: "dl", label: "E", x: -2.6, y: 1 },
39012
+ { id: "NT", pos: "dl", label: "N", x: 0, y: 1 },
39013
+ { id: "DE_S", pos: "dl", label: "E", x: 2.6, y: 1 },
39014
+ { id: "WOLB", pos: "lb", label: "W", x: -5.4, y: 3.6 },
39015
+ { id: "WILB", pos: "lb", label: "M", x: -1.6, y: 4.6 },
39016
+ { id: "SILB", pos: "lb", label: "M", x: 1.6, y: 4.6 },
39017
+ { id: "SOLB", pos: "lb", label: "S", x: s * 5.4, y: 3.6 }
39018
+ ];
39019
+ }
39020
+ function defensePreset(scheme, side) {
39021
+ const s = side === "right" ? 1 : -1;
39022
+ const cb = (sign, y, label = "C") => ({ id: sign < 0 ? "LCB" : "RCB", pos: "db", label, x: sign * K.wrSplit, y });
39023
+ let front;
39024
+ let cover;
39025
+ let showZones = true;
39026
+ if (scheme === "3-4") {
39027
+ front = front34(side);
39028
+ cover = "cover-2";
39029
+ showZones = false;
39030
+ } else if (scheme === "4-4") {
39031
+ front = [...front43(side), { id: "SS", pos: "s", label: "$", x: s * 6, y: 4.6 }];
39032
+ cover = "cover-3";
39033
+ } else if (scheme === "nickel") {
39034
+ front = front43(side).slice(0, 6);
39035
+ cover = "cover-2";
39036
+ } else if (scheme === "dime") {
39037
+ front = front43(side).slice(0, 5);
39038
+ cover = "cover-3";
39039
+ } else if (scheme.startsWith("cover")) {
39040
+ front = front43(side);
39041
+ cover = scheme;
39042
+ } else {
39043
+ front = front43(side);
39044
+ cover = "cover-2";
39045
+ showZones = false;
39046
+ }
39047
+ const sec = [];
39048
+ const zones = [];
39049
+ switch (cover) {
39050
+ case "cover-0":
39051
+ sec.push(cb(-1, 5.5), cb(1, 5.5), { id: "FS", pos: "s", label: "F", x: -6, y: 5 }, { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 });
39052
+ break;
39053
+ case "cover-1":
39054
+ sec.push(cb(-1, 6), cb(1, 6), { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 }, { id: "FS", pos: "s", label: "F", x: 0, y: 13.5 });
39055
+ zones.push({ x: 0, y: 13.5, rx: 5, ry: 3.5, label: "deep middle" });
39056
+ break;
39057
+ case "cover-2":
39058
+ sec.push(cb(-1, 5), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -9, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
39059
+ zones.push({ x: -9, y: 12.5, rx: 9, ry: 4, label: "deep half" }, { x: 9, y: 12.5, rx: 9, ry: 4, label: "deep half" });
39060
+ break;
39061
+ case "cover-3":
39062
+ sec.push(cb(-1, 11.5), cb(1, 11.5), { id: "FS", pos: "s", label: "F", x: 0, y: 14 }, { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 });
39063
+ zones.push({ x: -13.5, y: 12, rx: 5.8, ry: 3.6, label: "deep \u2153" }, { x: 0, y: 14.5, rx: 5.8, ry: 3.6, label: "deep \u2153" }, { x: 13.5, y: 12, rx: 5.8, ry: 3.6, label: "deep \u2153" });
39064
+ break;
39065
+ case "cover-4":
39066
+ sec.push(cb(-1, 10), cb(1, 10), { id: "FS", pos: "s", label: "F", x: -6, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 6, y: 12.5 });
39067
+ zones.push({ x: -13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: -5, y: 13, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 5, y: 13, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" });
39068
+ break;
39069
+ case "cover-6":
39070
+ sec.push(cb(-1, 10), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -7, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
39071
+ zones.push({ x: -7, y: 12.5, rx: 7, ry: 4, label: "deep \xBC" }, { x: -13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 9, y: 12.5, rx: 9, ry: 4, label: "deep \xBD" });
39072
+ break;
39073
+ default:
39074
+ sec.push(cb(-1, 5), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -9, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
39075
+ }
39076
+ return { defenders: [...front, ...sec].slice(0, 11), zones: showZones ? zones : [] };
39077
+ }
39078
+ function passRoute(named, depth, outSign, inSign) {
39079
+ const d = depth;
39080
+ switch (named) {
39081
+ case "go":
39082
+ case "fly":
39083
+ case "streak":
39084
+ case "vertical":
39085
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 16 }];
39086
+ case "seam":
39087
+ return [{ x: 0, y: 0 }, { x: inSign * 0.5, y: d ?? 14 }];
39088
+ case "slant":
39089
+ return [{ x: 0, y: 0 }, { x: 0, y: 1.6 }, { x: inSign * 4.5, y: 6 }];
39090
+ case "flat":
39091
+ return [{ x: 0, y: 0 }, { x: outSign * 1.5, y: 1 }, { x: outSign * 5.5, y: 2.2 }];
39092
+ case "hitch":
39093
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 5.5 }, { x: inSign * 1, y: (d ?? 5.5) - 1.2 }];
39094
+ case "out":
39095
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: outSign * 5.5, y: d ?? 10 }];
39096
+ case "in":
39097
+ case "dig":
39098
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: inSign * 6.5, y: d ?? 10 }];
39099
+ case "curl":
39100
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 12 }, { x: inSign * 1.6, y: (d ?? 12) - 1.6 }];
39101
+ case "comeback":
39102
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 14 }, { x: outSign * 2.2, y: (d ?? 14) - 2.4 }];
39103
+ case "corner":
39104
+ case "flag":
39105
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: outSign * 5.5, y: (d ?? 10) + 5 }];
39106
+ case "post":
39107
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: inSign * 5.5, y: (d ?? 10) + 6 }];
39108
+ case "wheel":
39109
+ return [{ x: 0, y: 0 }, { x: outSign * 3, y: 1 }, { x: outSign * 4, y: d ?? 11 }];
39110
+ case "cross":
39111
+ case "drag":
39112
+ return [{ x: 0, y: 0 }, { x: 0, y: 1.5 }, { x: inSign * 14, y: 3.2 }];
39113
+ case "screen":
39114
+ return [{ x: 0, y: 0 }, { x: 0, y: -1 }, { x: outSign * 4.5, y: -1 }];
39115
+ default:
39116
+ return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }];
39117
+ }
39118
+ }
39119
+ function runPath(named, start, dir) {
39120
+ const k = dirSign(dir);
39121
+ switch (named) {
39122
+ case "dive":
39123
+ case "trap":
39124
+ return [start, { x: k * 1, y: 0.5 }, { x: k * 1.6, y: 3.2 }];
39125
+ case "iso":
39126
+ return [start, { x: k * 0.4, y: 0.5 }, { x: k * 0.8, y: 3.4 }];
39127
+ case "power":
39128
+ return [start, { x: start.x + k * 1.4, y: start.y + 1.5 }, { x: k * 2.6, y: 0.6 }, { x: k * 3.6, y: 4.2 }];
39129
+ case "counter":
39130
+ return [start, { x: -k * 1.6, y: start.y + 0.3 }, { x: k * 2.2, y: 0.6 }, { x: k * 3.2, y: 4.2 }];
39131
+ case "sweep":
39132
+ case "toss":
39133
+ return [start, { x: k * 5, y: start.y + 0.6 }, { x: k * 7, y: 1.2 }, { x: k * 7, y: 5 }];
39134
+ case "draw":
39135
+ return [start, { x: 0, y: start.y + 1.5 }, { x: k * 1, y: 3.4 }];
39136
+ default:
39137
+ return [start, { x: k * 1.4, y: 3.2 }];
39138
+ }
39139
+ }
39140
+ var footballModule = {
39141
+ scale: K.scale,
39142
+ yUp: true,
39143
+ buildPlayers(ast) {
39144
+ const players = [];
39145
+ const byId = /* @__PURE__ */ new Map();
39146
+ const add = (p) => {
39147
+ if (byId.has(p.id)) players[byId.get(p.id)] = p;
39148
+ else {
39149
+ byId.set(p.id, players.length);
39150
+ players.push(p);
39151
+ }
39152
+ };
39153
+ if (ast.formation) for (const sl of formationRoster(ast.formation, ast.formationSide)) add({ id: sl.id, side: "offense", pos: sl.pos, label: sl.label, x: sl.x, y: sl.y });
39154
+ if (ast.defense) for (const d of defensePreset(ast.defense, ast.formationSide).defenders) add({ id: d.id, side: "defense", pos: d.pos, label: d.label, x: d.x, y: d.y });
39155
+ for (const p of ast.players) {
39156
+ const at = p.at ?? (byId.has(p.id) ? { x: players[byId.get(p.id)].x, y: players[byId.get(p.id)].y } : { x: 0, y: 0 });
39157
+ add({ id: p.id, side: p.side, pos: p.pos, label: p.label, x: at.x, y: at.y });
39158
+ }
39159
+ return players;
39160
+ },
39161
+ buildZones(ast) {
39162
+ const zones = [];
39163
+ if (ast.defense) zones.push(...defensePreset(ast.defense, ast.formationSide).zones);
39164
+ for (const z of ast.zones) zones.push({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label });
39165
+ return zones;
39166
+ },
39167
+ resolveNamed(m, src, players, byId) {
39168
+ const endFor2 = (kind) => m.end ?? ("arrow");
39169
+ if (m.kind === "pull") {
39170
+ const k = dirSign(m.dir ?? "right");
39171
+ const offTackle = 2 * K.olSplit + 0.8;
39172
+ const pts = [{ x: src.x, y: 0 }, { x: src.x, y: -1.3 }, { x: k * offTackle, y: -1.3 }, { x: k * offTackle, y: 1.6 }];
39173
+ return { player: m.player, kind: "pull", style: "solid", points: pts.map(round22), end: "arrow" };
39174
+ }
39175
+ if ((m.kind === "run" || m.kind === "route") && m.named) {
39176
+ if (m.kind === "run") {
39177
+ return { player: m.player, kind: "run", style: "solid", points: runPath(m.named, src, m.dir ?? "right").map(round22), end: "arrow" };
39178
+ }
39179
+ const outSign = m.dir ? dirSign(m.dir) : src.x >= 0 ? 1 : -1;
39180
+ const rel = passRoute(m.named, m.depth, outSign, -outSign);
39181
+ return { player: m.player, kind: "route", style: "solid", points: rel.map((p) => round22({ x: src.x + p.x, y: src.y + p.y })), end: endFor2() };
39182
+ }
39183
+ return null;
39184
+ },
39185
+ bounds(ast, players, moves, zones) {
39186
+ let minX = -20, maxX = K.minHalfWidth, minY = -7, maxY = 6;
39187
+ const ext = (x, y) => {
39188
+ minX = Math.min(minX, x);
39189
+ maxX = Math.max(maxX, x);
39190
+ minY = Math.min(minY, y);
39191
+ maxY = Math.max(maxY, y);
39192
+ };
39193
+ for (const p of players) ext(p.x, p.y);
39194
+ for (const mv of moves) for (const pt of mv.points) ext(pt.x, pt.y);
39195
+ for (const z of zones) {
39196
+ ext(z.x - z.rx, z.y - z.ry);
39197
+ ext(z.x + z.rx, z.y + z.ry);
39198
+ }
39199
+ if (ast.toGoal !== void 0) maxY = Math.max(maxY, ast.toGoal + 11);
39200
+ const m = K.margin;
39201
+ return { minX: minX - m, maxX: maxX + m, minY: minY - m, maxY: maxY + m };
39202
+ },
39203
+ drawField(lay, ctx, t) {
39204
+ const b = lay.bounds;
39205
+ const parts = [];
39206
+ const x1 = ctx.X(b.minX), x2 = ctx.X(b.maxX);
39207
+ if (lay.toGoal !== void 0) {
39208
+ const gl = lay.toGoal;
39209
+ const ezTop = Math.min(gl + 10, b.maxY);
39210
+ parts.push(rect({ class: "sx-pb-endzone", x: r25(x1), y: r25(ctx.Y(ezTop)), width: r25((b.maxX - b.minX) * K.scale), height: r25((ezTop - gl) * K.scale) }));
39211
+ parts.push(line({ class: "sx-pb-goalline", x1: r25(x1), y1: r25(ctx.Y(gl)), x2: r25(x2), y2: r25(ctx.Y(gl)) }));
39212
+ const gy = ctx.Y(Math.min(gl + 10, b.maxY) - 0.2);
39213
+ const cx = ctx.X(0);
39214
+ const up = ctx.px(2.2);
39215
+ const cross = ctx.px(3);
39216
+ parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx), y1: r25(gy), x2: r25(cx), y2: r25(gy + ctx.px(1)) }));
39217
+ parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx - cross / 2), y1: r25(gy), x2: r25(cx + cross / 2), y2: r25(gy) }));
39218
+ parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx - cross / 2), y1: r25(gy), x2: r25(cx - cross / 2), y2: r25(gy - up) }));
39219
+ parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx + cross / 2), y1: r25(gy), x2: r25(cx + cross / 2), y2: r25(gy - up) }));
39220
+ }
39221
+ const startY = Math.ceil(b.minY / 5) * 5;
39222
+ for (let yd = startY; yd <= b.maxY; yd += 5) {
39223
+ if (Math.abs(yd) < 1e-6) continue;
39224
+ if (lay.toGoal !== void 0 && yd > lay.toGoal + 0.01) continue;
39225
+ parts.push(line({ class: "sx-pb-yard", x1: r25(x1), y1: r25(ctx.Y(yd)), x2: r25(x2), y2: r25(ctx.Y(yd)) }));
39226
+ if (lay.toGoal !== void 0) {
39227
+ const yardNum = lay.toGoal - yd;
39228
+ if (yardNum > 0 && yardNum % 10 === 0) {
39229
+ parts.push(text({ class: "sx-pb-yardnum", x: r25(x1 + 12), y: r25(ctx.Y(yd) + 4), "text-anchor": "middle" }, String(yardNum)));
39230
+ parts.push(text({ class: "sx-pb-yardnum", x: r25(x2 - 12), y: r25(ctx.Y(yd) + 4), "text-anchor": "middle" }, String(yardNum)));
39231
+ }
39232
+ }
39233
+ }
39234
+ if (lay.hash !== "none") {
39235
+ const hx = lay.hash === "college" ? K.hashCollege : K.hashNfl;
39236
+ const top = lay.toGoal !== void 0 ? Math.min(b.maxY, lay.toGoal) : b.maxY;
39237
+ for (let yd = Math.ceil(b.minY); yd <= top; yd += 1) {
39238
+ for (const sgn of [-1, 1]) {
39239
+ const xc = ctx.X(sgn * hx);
39240
+ parts.push(line({ class: "sx-pb-hash", x1: r25(xc - 2), y1: r25(ctx.Y(yd)), x2: r25(xc + 2), y2: r25(ctx.Y(yd)) }));
39241
+ }
39242
+ }
39243
+ }
39244
+ parts.push(line({ class: "sx-pb-los", x1: r25(x1), y1: r25(ctx.Y(0)), x2: r25(x2), y2: r25(ctx.Y(0)) }));
39245
+ return group({ class: "sx-pb-field-g" }, parts);
39246
+ },
39247
+ legend(lay) {
39248
+ const items = [
39249
+ { kind: "offense", label: "Offense" },
39250
+ { kind: "defense", label: "Defense" },
39251
+ { kind: "run", label: "Route / run" },
39252
+ { kind: "screen", label: "Block" },
39253
+ { kind: "motion", label: "Motion" }
39254
+ ];
39255
+ if (lay.zones.length) items.push({ kind: "zone", label: "Zone" });
39256
+ return items;
39257
+ }
39258
+ };
39259
+
39260
+ // src/diagrams/playbook/geometry/basketball.ts
39261
+ var K2 = {
39262
+ scale: 11,
39263
+ // x ∈ [-25, 25]
39264
+ half: 47,
39265
+ rimY: 5.25,
39266
+ laneHalf: 8,
39267
+ // NBA lane half-width
39268
+ ftY: 15,
39269
+ ftR: 6,
39270
+ threeR: 23.75,
39271
+ cornerX: 22,
39272
+ cornerMeetY: 14.2,
39273
+ restrictedR: 4,
39274
+ centerR: 6,
39275
+ margin: 2.5
39276
+ };
39277
+ var r26 = (n) => Math.round(n * 100) / 100;
39278
+ var LANDMARKS = {
39279
+ top: { x: 0, y: 28 },
39280
+ rslot: { x: 8, y: 26 },
39281
+ lslot: { x: -8, y: 26 },
39282
+ slot: { x: 8, y: 26 },
39283
+ rwing: { x: 19, y: 17 },
39284
+ lwing: { x: -19, y: 17 },
39285
+ wing: { x: 19, y: 17 },
39286
+ rcorner: { x: 22, y: 3 },
39287
+ lcorner: { x: -22, y: 3 },
39288
+ corner: { x: 22, y: 3 },
39289
+ "rshort-corner": { x: 12, y: 2 },
39290
+ "lshort-corner": { x: -12, y: 2 },
39291
+ relbow: { x: 8, y: 15 },
39292
+ lelbow: { x: -8, y: 15 },
39293
+ elbow: { x: 8, y: 15 },
39294
+ rblock: { x: 8, y: 7 },
39295
+ lblock: { x: -8, y: 7 },
39296
+ block: { x: 8, y: 7 },
39297
+ rdunker: { x: 9, y: 4 },
39298
+ ldunker: { x: -9, y: 4 },
39299
+ "high-post": { x: 0, y: 15 },
39300
+ ft: { x: 0, y: 15 },
39301
+ "free-throw": { x: 0, y: 15 },
39302
+ "rlow-post": { x: 9, y: 8 },
39303
+ "llow-post": { x: -9, y: 8 },
39304
+ rim: { x: 0, y: 5.25 },
39305
+ basket: { x: 0, y: 5.25 },
39306
+ hoop: { x: 0, y: 5.25 },
39307
+ paint: { x: 0, y: 10 }
39308
+ };
39309
+ function setRoster(formation) {
39310
+ const L = LANDMARKS;
39311
+ const at = (lm) => L[lm];
39312
+ const slots = (pts) => pts.map((p, i) => ({ id: String(i + 1), label: String(i + 1), x: p.x, y: p.y }));
39313
+ switch (formation) {
39314
+ case "horns":
39315
+ return slots([at("top"), at("lcorner"), at("rcorner"), at("lelbow"), at("relbow")]);
39316
+ case "1-4-high":
39317
+ return slots([at("top"), at("lwing"), at("rwing"), at("lelbow"), at("relbow")]);
39318
+ case "1-4-low":
39319
+ return slots([at("top"), at("lcorner"), at("rcorner"), at("lblock"), at("rblock")]);
39320
+ case "box":
39321
+ return slots([at("top"), at("lelbow"), at("relbow"), at("lblock"), at("rblock")]);
39322
+ case "spread-pnr":
39323
+ return slots([at("top"), at("lcorner"), at("rcorner"), at("lwing"), { x: 8, y: 24 }]);
39324
+ case "4-out":
39325
+ return slots([at("top"), at("lwing"), at("rwing"), at("lcorner"), at("rblock")]);
39326
+ case "5-out":
39327
+ default:
39328
+ return slots([at("top"), at("lwing"), at("rwing"), at("lcorner"), at("rcorner")]);
39329
+ }
39330
+ }
39331
+ function defenseRoster(scheme, offense) {
39332
+ const zones = [];
39333
+ if (scheme === "man") {
39334
+ const defenders2 = offense.filter((p) => p.side === "offense").slice(0, 5).map((o) => {
39335
+ const dx = -o.x * 0.12;
39336
+ const dy = (K2.rimY - o.y) * 0.18;
39337
+ return { id: "X" + o.id, label: "X" + o.label, x: o.x + dx, y: o.y + dy };
39338
+ });
39339
+ return { defenders: defenders2, zones };
39340
+ }
39341
+ let pts;
39342
+ if (scheme === "zone-3-2") pts = [{ x: 0, y: 20 }, { x: -13, y: 17 }, { x: 13, y: 17 }, { x: -8, y: 8 }, { x: 8, y: 8 }];
39343
+ else if (scheme === "zone-1-3-1") pts = [{ x: 0, y: 22 }, { x: -12, y: 14 }, { x: 0, y: 13 }, { x: 12, y: 14 }, { x: 0, y: 5.5 }];
39344
+ else pts = [{ x: -7, y: 18 }, { x: 7, y: 18 }, { x: 0, y: 9 }, { x: -13, y: 6 }, { x: 13, y: 6 }];
39345
+ const defenders = pts.map((p, i) => ({ id: "X" + (i + 1), label: "X", x: p.x, y: p.y }));
39346
+ return { defenders, zones };
39347
+ }
39348
+ var basketballModule = {
39349
+ scale: K2.scale,
39350
+ yUp: false,
39351
+ buildPlayers(ast) {
39352
+ const players = [];
39353
+ const byId = /* @__PURE__ */ new Map();
39354
+ const add = (p) => {
39355
+ if (byId.has(p.id)) players[byId.get(p.id)] = p;
39356
+ else {
39357
+ byId.set(p.id, players.length);
39358
+ players.push(p);
39359
+ }
39360
+ };
39361
+ for (const sl of setRoster(ast.formation)) add({ id: sl.id, side: "offense", pos: "o", label: sl.label, x: sl.x, y: sl.y });
39362
+ if (ast.defense) for (const d of defenseRoster(ast.defense, players).defenders) add({ id: d.id, side: "defense", pos: "x", label: d.label, x: d.x, y: d.y });
39363
+ for (const p of ast.players) {
39364
+ const at = p.at ?? (byId.has(p.id) ? { x: players[byId.get(p.id)].x, y: players[byId.get(p.id)].y } : { x: 0, y: 0 });
39365
+ add({ id: p.id, side: p.side, pos: p.pos === "c" || p.pos === "ol" ? "o" : p.pos, label: p.label, x: at.x, y: at.y });
39366
+ }
39367
+ return players;
39368
+ },
39369
+ buildZones(ast) {
39370
+ return ast.zones.map((z) => ({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label }));
39371
+ },
39372
+ resolveLandmark(name) {
39373
+ return LANDMARKS[name.toLowerCase()] ?? null;
39374
+ },
39375
+ bounds(_ast, players, moves, zones) {
39376
+ let minX = -25, maxX = 25, minY = 0, maxY = 30;
39377
+ const ext = (x, y) => {
39378
+ minX = Math.min(minX, x);
39379
+ maxX = Math.max(maxX, x);
39380
+ minY = Math.min(minY, y);
39381
+ maxY = Math.max(maxY, y);
39382
+ };
39383
+ for (const p of players) ext(p.x, p.y);
39384
+ for (const mv of moves) for (const pt of mv.points) ext(pt.x, pt.y);
39385
+ for (const z of zones) {
39386
+ ext(z.x - z.rx, z.y - z.ry);
39387
+ ext(z.x + z.rx, z.y + z.ry);
39388
+ }
39389
+ const m = K2.margin;
39390
+ return { minX: Math.min(-25, minX) - m * 0.4, maxX: Math.max(25, maxX) + m * 0.4, minY: Math.min(0, minY) - m * 0.4, maxY: Math.min(K2.half, maxY + m * 0.6) };
39391
+ },
39392
+ drawField(_lay, ctx, t) {
39393
+ const parts = [];
39394
+ const X = ctx.X, Y = ctx.Y, px = ctx.px;
39395
+ parts.push(rect({ class: "sx-pb-court-line", fill: "none", x: r26(X(-8)), y: r26(Y(0)), width: r26(px(2 * K2.laneHalf)), height: r26(px(K2.ftY)) }));
39396
+ parts.push(circle({ class: "sx-pb-court-line", fill: "none", cx: r26(X(0)), cy: r26(Y(K2.ftY)), r: r26(px(K2.ftR)) }));
39397
+ parts.push(line({ class: "sx-pb-court-line", x1: r26(X(-3)), y1: r26(Y(4)), x2: r26(X(3)), y2: r26(Y(4)) }));
39398
+ parts.push(circle({ class: "sx-pb-rim", fill: "none", cx: r26(X(0)), cy: r26(Y(K2.rimY)), r: r26(px(0.75)) }));
39399
+ parts.push(path({ class: "sx-pb-court-line", fill: "none", d: `M ${r26(X(-4))} ${r26(Y(K2.rimY))} A ${r26(px(K2.restrictedR))} ${r26(px(K2.restrictedR))} 0 0 0 ${r26(X(K2.restrictedR))} ${r26(Y(K2.rimY))}` }));
39400
+ parts.push(path({
39401
+ class: "sx-pb-court-line",
39402
+ fill: "none",
39403
+ d: `M ${r26(X(-22))} ${r26(Y(0))} L ${r26(X(-22))} ${r26(Y(K2.cornerMeetY))} A ${r26(px(K2.threeR))} ${r26(px(K2.threeR))} 0 0 0 ${r26(X(K2.cornerX))} ${r26(Y(K2.cornerMeetY))} L ${r26(X(K2.cornerX))} ${r26(Y(0))}`
39404
+ }));
39405
+ parts.push(path({ class: "sx-pb-court-line", fill: "none", d: `M ${r26(X(-6))} ${r26(Y(K2.half))} A ${r26(px(K2.centerR))} ${r26(px(K2.centerR))} 0 0 1 ${r26(X(K2.centerR))} ${r26(Y(K2.half))}` }));
39406
+ return group({ class: "sx-pb-field-g" }, parts);
39407
+ },
39408
+ legend() {
39409
+ return [
39410
+ { kind: "offense", label: "Offense (1\u20135)" },
39411
+ { kind: "defense", label: "Defense (X)" },
39412
+ { kind: "run", label: "Cut" },
39413
+ { kind: "pass", label: "Pass" },
39414
+ { kind: "dribble", label: "Dribble" },
39415
+ { kind: "screen", label: "Screen" }
39416
+ ];
39417
+ }
39418
+ };
39419
+
39420
+ // src/diagrams/playbook/geometry/soccer.ts
39421
+ var K3 = {
39422
+ scale: 8,
39423
+ L: 105,
39424
+ W: 68,
39425
+ circleR: 9.15,
39426
+ paDepth: 16.5,
39427
+ paHalf: 20.16,
39428
+ // 40.32 / 2
39429
+ gaDepth: 5.5,
39430
+ gaHalf: 9.16,
39431
+ // 18.32 / 2
39432
+ penDist: 11,
39433
+ goalHalf: 3.66,
39434
+ // 7.32 / 2
39435
+ cornerR: 1,
39436
+ margin: 3
39437
+ };
39438
+ var r27 = (n) => Math.round(n * 100) / 100;
39439
+ function formationRoster2(formation) {
39440
+ const p = (id, x, y, gk2 = false) => ({ id, label: id, x, y, gk: gk2 });
39441
+ const back4 = [p("2", 20, 12), p("4", 20, 27), p("5", 20, 41), p("3", 20, 56)];
39442
+ const gk = p("1", 5, 34, true);
39443
+ switch (formation) {
39444
+ case "4-4-2":
39445
+ return [gk, ...back4, p("7", 50, 10), p("6", 50, 27), p("8", 50, 41), p("11", 50, 58), p("9", 80, 28), p("10", 80, 40)];
39446
+ case "4-2-3-1":
39447
+ return [gk, ...back4, p("6", 40, 28), p("8", 40, 40), p("7", 65, 12), p("10", 65, 34), p("11", 65, 56), p("9", 85, 34)];
39448
+ case "4-5-1":
39449
+ return [gk, ...back4, p("6", 48, 27), p("8", 48, 41), p("7", 52, 10), p("11", 52, 58), p("10", 62, 34), p("9", 85, 34)];
39450
+ case "4-4-1-1":
39451
+ return [gk, ...back4, p("7", 50, 10), p("6", 50, 27), p("8", 50, 41), p("11", 50, 58), p("10", 68, 34), p("9", 84, 34)];
39452
+ case "3-5-2":
39453
+ return [gk, p("4", 20, 20), p("5", 20, 34), p("6", 20, 48), p("2", 52, 6), p("3", 52, 62), p("8", 50, 25), p("10", 55, 43), p("7", 45, 34), p("9", 82, 28), p("11", 82, 40)];
39454
+ case "3-4-3":
39455
+ return [gk, p("4", 20, 20), p("5", 20, 34), p("6", 20, 48), p("2", 48, 8), p("8", 48, 28), p("10", 48, 40), p("3", 48, 60), p("7", 82, 14), p("9", 82, 34), p("11", 82, 54)];
39456
+ case "4-3-3":
39457
+ default:
39458
+ return [gk, ...back4, p("6", 42, 34), p("8", 52, 24), p("10", 52, 44), p("7", 82, 12), p("9", 82, 34), p("11", 82, 56)];
39459
+ }
39460
+ }
39461
+ function opponentBlock(scheme) {
39462
+ const cx = scheme === "high-press" ? 45 : scheme === "mid-block" ? 70 : 88;
39463
+ const p = (i, x, y) => ({ id: "X" + i, label: "X", x, y });
39464
+ return [
39465
+ p(1, cx + 12, 34),
39466
+ p(2, cx, 12),
39467
+ p(3, cx, 27),
39468
+ p(4, cx, 41),
39469
+ p(5, cx, 56),
39470
+ p(6, cx - 14, 14),
39471
+ p(7, cx - 14, 28),
39472
+ p(8, cx - 14, 40),
39473
+ p(9, cx - 14, 54),
39474
+ p(10, cx - 26, 28),
39475
+ p(11, cx - 26, 40)
39476
+ ];
39477
+ }
39478
+ var LANDMARKS2 = {
39479
+ center: { x: 52.5, y: 34 },
39480
+ "centre-spot": { x: 52.5, y: 34 },
39481
+ box: { x: 96, y: 34 },
39482
+ "top-box": { x: 88.5, y: 34 },
39483
+ d: { x: 84.85, y: 34 },
39484
+ "penalty-arc": { x: 84.85, y: 34 },
39485
+ "penalty-spot": { x: 94, y: 34 },
39486
+ "near-post": { x: 105, y: 30.5 },
39487
+ "far-post": { x: 105, y: 37.7 },
39488
+ goal: { x: 105, y: 34 },
39489
+ "rcorner": { x: 105, y: 2 },
39490
+ "lcorner": { x: 105, y: 66 },
39491
+ "six-yard": { x: 101, y: 34 }
39492
+ };
39493
+ var soccerModule = {
39494
+ scale: K3.scale,
39495
+ yUp: false,
39496
+ buildPlayers(ast) {
39497
+ const players = [];
39498
+ const byId = /* @__PURE__ */ new Map();
39499
+ const add = (pl2) => {
39500
+ if (byId.has(pl2.id)) players[byId.get(pl2.id)] = pl2;
39501
+ else {
39502
+ byId.set(pl2.id, players.length);
39503
+ players.push(pl2);
39504
+ }
39505
+ };
39506
+ for (const sl of formationRoster2(ast.formation)) add({ id: sl.id, side: "offense", pos: sl.gk ? "gk" : "o", label: sl.label, x: sl.x, y: sl.y });
39507
+ if (ast.defense) for (const d of opponentBlock(ast.defense)) add({ id: d.id, side: "defense", pos: "x", label: d.label, x: d.x, y: d.y });
39508
+ for (const pl2 of ast.players) {
39509
+ const at = pl2.at ?? (byId.has(pl2.id) ? { x: players[byId.get(pl2.id)].x, y: players[byId.get(pl2.id)].y } : { x: 0, y: 0 });
39510
+ add({ id: pl2.id, side: pl2.side, pos: pl2.pos === "c" || pl2.pos === "ol" ? "o" : pl2.pos, label: pl2.label, x: at.x, y: at.y });
39511
+ }
39512
+ return players;
39513
+ },
39514
+ buildZones(ast) {
39515
+ return ast.zones.map((z) => ({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label }));
39516
+ },
39517
+ resolveLandmark(name) {
39518
+ return LANDMARKS2[name.toLowerCase()] ?? null;
39519
+ },
39520
+ bounds(ast) {
39521
+ const m = K3.margin;
39522
+ if (ast.view === "half") return { minX: 52.5 - m, maxX: K3.L + m, minY: -m, maxY: K3.W + m };
39523
+ return { minX: -m, maxX: K3.L + m, minY: -m, maxY: K3.W + m };
39524
+ },
39525
+ drawField(_lay, ctx, t) {
39526
+ const X = ctx.X, Y = ctx.Y, px = ctx.px;
39527
+ const parts = [];
39528
+ const ln = (x1, y1, x2, y2) => line({ class: "sx-pb-pitch-line", x1: r27(X(x1)), y1: r27(Y(y1)), x2: r27(X(x2)), y2: r27(Y(y2)) });
39529
+ const bands = 10;
39530
+ for (let i = 0; i < bands; i++) {
39531
+ if (i % 2 === 0) continue;
39532
+ parts.push(rect({ class: "sx-pb-stripe", x: r27(X(i * K3.L / bands)), y: r27(Y(0)), width: r27(px(K3.L / bands)), height: r27(px(K3.W)) }));
39533
+ }
39534
+ const cr = px(K3.cornerR);
39535
+ const corners = [[0, 0, 0], [K3.L, 0, 1], [0, K3.W, 2], [K3.L, K3.W, 3]];
39536
+ for (const [cxF, cyF, q] of corners) {
39537
+ const cx = X(cxF), cy = Y(cyF);
39538
+ const sx = cxF === 0 ? 1 : -1;
39539
+ const sy = cyF === 0 ? 1 : -1;
39540
+ parts.push(path({ class: "sx-pb-pitch-line", fill: "none", d: `M ${r27(cx + sx * cr)} ${r27(cy)} A ${r27(cr)} ${r27(cr)} 0 0 ${q === 1 || q === 2 ? 1 : 0} ${r27(cx)} ${r27(cy + sy * cr)}` }));
39541
+ }
39542
+ parts.push(ln(52.5, 0, 52.5, K3.W));
39543
+ parts.push(circle({ class: "sx-pb-pitch-line", fill: "none", cx: r27(X(52.5)), cy: r27(Y(34)), r: r27(px(K3.circleR)) }));
39544
+ parts.push(circle({ class: "sx-pb-pitch-dot", cx: r27(X(52.5)), cy: r27(Y(34)), r: 1.6 }));
39545
+ const topY = (half) => Math.min(Y(34 - half), Y(34 + half));
39546
+ for (const end of [0, 1]) {
39547
+ const gx = end === 0 ? 0 : K3.L;
39548
+ const sgn = end === 0 ? 1 : -1;
39549
+ parts.push(rect({ class: "sx-pb-pitch-line", fill: "none", x: r27(X(Math.min(gx, gx + sgn * K3.paDepth))), y: r27(topY(K3.paHalf)), width: r27(px(K3.paDepth)), height: r27(px(2 * K3.paHalf)) }));
39550
+ parts.push(rect({ class: "sx-pb-pitch-line", fill: "none", x: r27(X(Math.min(gx, gx + sgn * K3.gaDepth))), y: r27(topY(K3.gaHalf)), width: r27(px(K3.gaDepth)), height: r27(px(2 * K3.gaHalf)) }));
39551
+ const spotX = gx + sgn * K3.penDist;
39552
+ parts.push(circle({ class: "sx-pb-pitch-dot", cx: r27(X(spotX)), cy: r27(Y(34)), r: 1.4 }));
39553
+ const edgeX = gx + sgn * K3.paDepth;
39554
+ const dy = Math.sqrt(K3.circleR * K3.circleR - (K3.paDepth - K3.penDist) * (K3.paDepth - K3.penDist));
39555
+ const sweep = end === 0 ? 1 : 0;
39556
+ parts.push(path({ class: "sx-pb-pitch-line", fill: "none", d: `M ${r27(X(edgeX))} ${r27(Y(34 - dy))} A ${r27(px(K3.circleR))} ${r27(px(K3.circleR))} 0 0 ${sweep} ${r27(X(edgeX))} ${r27(Y(34 + dy))}` }));
39557
+ const goalX = gx - sgn * 2.2;
39558
+ parts.push(rect({ class: "sx-pb-goalbox", x: r27(X(Math.min(gx, goalX))), y: r27(topY(K3.goalHalf)), width: r27(px(2.2)), height: r27(px(2 * K3.goalHalf)) }));
39559
+ }
39560
+ return group({ class: "sx-pb-field-g" }, parts);
39561
+ },
39562
+ legend() {
39563
+ return [
39564
+ { kind: "offense", label: "Team" },
39565
+ { kind: "gk", label: "Keeper" },
39566
+ { kind: "defense", label: "Opponent" },
39567
+ { kind: "pass", label: "Pass" },
39568
+ { kind: "run", label: "Run" },
39569
+ { kind: "dribble", label: "Dribble" },
39570
+ { kind: "shot", label: "Shot" }
39571
+ ];
39572
+ }
39573
+ };
39574
+
39575
+ // src/diagrams/playbook/layout.ts
39576
+ var MODULES = {
39577
+ football: footballModule,
39578
+ basketball: basketballModule,
39579
+ soccer: soccerModule
39580
+ };
39581
+ function sportModule(sport) {
39582
+ return MODULES[sport];
39583
+ }
39584
+ var round23 = (p) => ({ x: Math.round(p.x * 100) / 100, y: Math.round(p.y * 100) / 100 });
39585
+ function styleFor(sport, kind) {
39586
+ if (kind === "dribble") return "wavy";
39587
+ if (kind === "shot") return sport === "soccer" ? "double" : "solid";
39588
+ if (kind === "pass") return sport === "soccer" ? "solid" : "dashed";
39589
+ if (kind === "motion") return "dashed";
39590
+ if (sport === "soccer" && (kind === "run" || kind === "cut" || kind === "move")) return "dashed";
39591
+ return "solid";
39592
+ }
39593
+ function endFor(kind, override) {
39594
+ if (override) return override;
39595
+ if (kind === "screen" || kind === "block") return "tee";
39596
+ if (kind === "handoff") return "none";
39597
+ return "arrow";
39598
+ }
39599
+ function findPlayer(ref, byId) {
39600
+ if (byId.has(ref)) return byId.get(ref);
39601
+ const lower = ref.toLowerCase();
39602
+ for (const [k, v] of byId) if (k.toLowerCase() === lower) return v;
39603
+ return void 0;
39604
+ }
39605
+ function resolveRef(token, byId, players, mod) {
39606
+ const coord = /^(-?\d*\.?\d+),(-?\d*\.?\d+)$/.exec(token);
39607
+ if (coord) return { x: Number(coord[1]), y: Number(coord[2]) };
39608
+ const pi = findPlayer(token, byId);
39609
+ if (pi !== void 0) return { x: players[pi].x, y: players[pi].y };
39610
+ const lm = mod.resolveLandmark?.(token);
39611
+ if (lm) return { x: lm.x, y: lm.y };
39612
+ return null;
39613
+ }
39614
+ function layoutPlaybook(ast) {
39615
+ const mod = MODULES[ast.sport];
39616
+ const errors = [];
39617
+ const warnings = [];
39618
+ const players = mod.buildPlayers(ast);
39619
+ const byId = /* @__PURE__ */ new Map();
39620
+ players.forEach((p, i) => byId.set(p.id, i));
39621
+ if (players.length === 0) {
39622
+ errors.push("no players \u2014 add a `formation`/set or explicit `player` statements");
39623
+ }
39624
+ const zones = mod.buildZones(ast, players);
39625
+ const moves = [];
39626
+ for (const m of ast.moves) {
39627
+ const srcIdx = findPlayer(m.player, byId);
39628
+ if (srcIdx === void 0) {
39629
+ warnings.push(`move on unknown player "${m.player}" \u2014 skipped`);
39630
+ continue;
39631
+ }
39632
+ const src = { x: players[srcIdx].x, y: players[srcIdx].y };
39633
+ const named = mod.resolveNamed?.(m, src, players, byId, warnings);
39634
+ if (named) {
39635
+ moves.push(named);
39636
+ continue;
39637
+ }
39638
+ const geom = resolveGeneric(m, src, byId, players, mod, ast.sport, warnings);
39639
+ if (geom) moves.push(geom);
39640
+ }
39641
+ const bounds = mod.bounds(ast, players, moves, zones);
39642
+ return {
39643
+ title: ast.title,
39644
+ sport: ast.sport,
39645
+ down: ast.down,
39646
+ distance: ast.distance,
39647
+ losYard: ast.losYard,
39648
+ toGoal: ast.toGoal,
39649
+ hash: ast.hash,
39650
+ view: ast.view === "half" ? "half" : "full",
39651
+ players,
39652
+ moves,
39653
+ zones,
39654
+ bounds,
39655
+ errors,
39656
+ warnings
39657
+ };
39658
+ }
39659
+ function resolveGeneric(m, src, byId, players, mod, sport, warnings) {
39660
+ const style = styleFor(sport, m.kind);
39661
+ if (m.points && m.points.length) {
39662
+ const pts = [{ x: src.x, y: src.y }];
39663
+ let cur = { x: src.x, y: src.y };
39664
+ for (const p of m.points) {
39665
+ if (p.ref) {
39666
+ const r6 = resolveRef(p.ref, byId, players, mod);
39667
+ if (!r6) {
39668
+ warnings.push(`${m.kind} ${m.player}: unknown destination "${p.ref}" \u2014 skipped`);
39669
+ return null;
39670
+ }
39671
+ cur = r6;
39672
+ } else if (p.rel) {
39673
+ cur = { x: cur.x + (p.x ?? 0), y: cur.y + (p.y ?? 0) };
39674
+ } else {
39675
+ cur = { x: p.x ?? cur.x, y: p.y ?? cur.y };
39676
+ }
39677
+ pts.push(cur);
39678
+ }
39679
+ return { player: m.player, kind: m.kind, style, points: pts.map(round23), end: endFor(m.kind, m.end) };
39680
+ }
39681
+ let dest = null;
39682
+ if (m.target) dest = resolveRef(m.target, byId, players, mod);
39683
+ else if (m.kind === "shot") dest = mod.resolveLandmark?.("rim") ?? mod.resolveLandmark?.("goal") ?? null;
39684
+ if (!dest) {
39685
+ warnings.push(`${m.kind} ${m.player}: needs a target/destination \u2014 skipped`);
39686
+ return null;
39687
+ }
39688
+ if (m.kind === "screen" || m.kind === "block") {
39689
+ const dx = dest.x - src.x, dy = dest.y - src.y;
39690
+ const len = Math.hypot(dx, dy) || 1;
39691
+ const gap = sport === "football" ? 0.6 : sport === "basketball" ? 1.2 : 1.6;
39692
+ const stop = Math.max(0, len - gap);
39693
+ const end = { x: src.x + dx / len * stop, y: src.y + dy / len * stop };
39694
+ return { player: m.player, kind: m.kind, style, points: [round23(src), round23(end)], end: "tee" };
39695
+ }
39696
+ return { player: m.player, kind: m.kind, style, points: [round23(src), round23(dest)], end: endFor(m.kind, m.end) };
39697
+ }
39698
+
39699
+ // src/diagrams/playbook/renderer.ts
39700
+ var r28 = (n) => Math.round(n * 100) / 100;
39701
+ function buildCss14(t) {
39702
+ return `
39703
+ .sx-pb { font-family: ${DEFAULT_FONT_FAMILY}; }
39704
+ .sx-pb-title { font: ${TITLE.weight} ${TITLE.size}px sans-serif; fill: ${t.text}; }
39705
+ .sx-pb-field, .sx-pb-turf { fill: ${t.surface}; }
39706
+ .sx-pb-court { fill: ${t.courtSurface}; }
39707
+ .sx-pb-surround { fill: ${t.surround}; }
39708
+ .sx-pb-surround-court { fill: ${t.courtSurround}; }
39709
+ .sx-pb-boundary { fill: none; stroke: ${t.lineBold}; stroke-width: 2.6; }
39710
+ .sx-pb-boundary-court { fill: none; stroke: ${t.courtLine}; stroke-width: 2.6; }
39711
+ .sx-pb-stripe { fill: ${t.surfaceAlt}; }
39712
+ .sx-pb-yard, .sx-pb-pitch-line, .sx-pb-goalbox { fill: none; stroke: ${t.lineSoft}; stroke-width: 1.4; }
39713
+ .sx-pb-court-line { fill: none; stroke: ${t.courtLine}; stroke-width: 1.5; }
39714
+ .sx-pb-hash { fill: none; stroke: ${t.lineSoft}; stroke-width: 1.2; }
39715
+ .sx-pb-los, .sx-pb-goalline { fill: none; stroke: ${t.lineBold}; stroke-width: 2.4; }
39716
+ .sx-pb-goalline { stroke: ${t.goalAccent}; }
39717
+ .sx-pb-goalpost { fill: none; stroke: ${t.goalAccent}; stroke-width: 2.4; stroke-linecap: round; }
39718
+ .sx-pb-endzone { fill: ${t.endzoneFill}; }
39719
+ .sx-pb-yardnum { font: 600 11px sans-serif; fill: ${t.surfaceText}; }
39720
+ .sx-pb-rim { stroke: ${t.rim}; stroke-width: 2.4; }
39721
+ .sx-pb-pitch-dot { fill: ${t.lineBold}; }
39722
+ .sx-pb-zone { fill: ${t.zoneFill}; stroke: ${t.zoneStroke}; stroke-width: 1.3; stroke-dasharray: 5 4; }
39723
+ .sx-pb-zone-text { font: 9px sans-serif; fill: ${t.zoneStroke}; }
39724
+ .sx-pb-move { fill: none; stroke: ${t.moveStroke}; stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; }
39725
+ .sx-pb-move-dash { fill: none; stroke: ${t.moveStroke}; stroke-width: 2.2; stroke-dasharray: 6 4; stroke-linejoin: round; stroke-linecap: round; }
39726
+ .sx-pb-shot { fill: none; stroke: ${t.shotStroke}; stroke-width: 3.4; stroke-linecap: round; }
39727
+ .sx-pb-motion { fill: none; stroke: ${t.motionStroke}; stroke-width: 1.9; stroke-dasharray: 4 3; }
39728
+ .sx-pb-move-fill { fill: ${t.moveStroke}; stroke: none; }
39729
+ .sx-pb-shot-fill { fill: ${t.shotStroke}; stroke: none; }
39730
+ .sx-pb-motion-fill { fill: ${t.motionStroke}; stroke: none; }
39731
+ .sx-pb-o { fill: ${t.offenseFill}; stroke: ${t.offenseStroke}; stroke-width: 2; }
39732
+ .sx-pb-gk { fill: ${t.gkFill}; stroke: ${t.offenseStroke}; stroke-width: 2; }
39733
+ .sx-pb-o-text { font: 700 10.5px sans-serif; fill: ${t.offenseLabel}; }
39734
+ .sx-pb-x { fill: none; stroke: ${t.defenseStroke}; stroke-width: 2.6; stroke-linecap: round; }
39735
+ .sx-pb-x-text { font: 700 9px sans-serif; fill: ${t.defenseStroke}; }
39736
+ .sx-pb-ball { fill: ${t.ballFill}; stroke: ${t.lineBold}; stroke-width: 0.8; }
39737
+ .sx-pb-anno { font: 600 12px sans-serif; fill: ${t.annotation}; }
39738
+ .sx-pb-legend { font: 11px sans-serif; fill: ${t.annotation}; }
39739
+ .sx-pb-error-box { fill: ${t.bg}; stroke: ${t.negative}; stroke-width: 1.5; }
39740
+ .sx-pb-error-title { font: 700 13px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
39741
+ .sx-pb-error-line { font: 12px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
39742
+ `.trim();
39743
+ }
39744
+ function renderErrorPanel2(lay, t) {
39745
+ const lines = lay.errors;
39746
+ const w = Math.max(520, ...lines.map((l) => l.length * 6.6 + 48));
39747
+ const h = 56 + lines.length * 19;
39748
+ return svgRoot(
39749
+ { viewBox: `0 0 ${r28(w)} ${h}`, width: r28(w), height: h, class: "sx-pb", role: "img" },
39750
+ [
39751
+ title(lay.title),
39752
+ desc(`Playbook validation failed with ${lines.length} error${lines.length === 1 ? "" : "s"}.`),
39753
+ el("style", {}, buildCss14(t)),
39754
+ rect({ class: "sx-pb-error-box", x: 1, y: 1, width: r28(w - 2), height: h - 2, rx: 6 }),
39755
+ text({ class: "sx-pb-error-title", x: 16, y: 26 }, `playbook: ${lines.length} validation error${lines.length === 1 ? "" : "s"}`),
39756
+ ...lines.map((e, i) => text({ class: "sx-pb-error-line", x: 16, y: 50 + i * 19 }, `\u26A0 ${e}`))
39757
+ ]
39758
+ );
39759
+ }
39760
+ function wavyPath(pts, amp = 3.4, wl = 11) {
39761
+ const flat = [];
39762
+ let acc = 0;
39763
+ for (let i = 1; i < pts.length; i++) {
39764
+ const a = pts[i - 1], b = pts[i];
39765
+ const dx = b.x - a.x, dy = b.y - a.y;
39766
+ const segLen = Math.hypot(dx, dy) || 1;
39767
+ const ux = dx / segLen, uy = dy / segLen;
39768
+ const nx = -uy, ny = ux;
39769
+ const lastSeg = i === pts.length - 1;
39770
+ const straightTail = lastSeg ? 9 : 0;
39771
+ const usable = Math.max(0, segLen - straightTail);
39772
+ const steps = Math.max(2, Math.round(usable / 2));
39773
+ for (let s = 0; s <= steps; s++) {
39774
+ const d = usable * s / steps;
39775
+ const off = amp * Math.sin(2 * Math.PI * (acc + d) / wl);
39776
+ flat.push({ x: a.x + ux * d + nx * off, y: a.y + uy * d + ny * off });
39777
+ }
39778
+ acc += usable;
39779
+ if (lastSeg) flat.push({ x: b.x, y: b.y });
39780
+ }
39781
+ return flat.map((p, i) => `${i === 0 ? "M" : "L"} ${r28(p.x)} ${r28(p.y)}`).join(" ");
39782
+ }
39783
+ function arrowHead4(pts, cls) {
39784
+ if (pts.length < 2) return "";
39785
+ const a = pts[pts.length - 2], b = pts[pts.length - 1];
39786
+ const ang = Math.atan2(b.y - a.y, b.x - a.x);
39787
+ const size = 8, wing = 0.42;
39788
+ const p2 = `${r28(b.x - size * Math.cos(ang - wing))},${r28(b.y - size * Math.sin(ang - wing))}`;
39789
+ const p3 = `${r28(b.x - size * Math.cos(ang + wing))},${r28(b.y - size * Math.sin(ang + wing))}`;
39790
+ return polygon({ class: cls, points: `${r28(b.x)},${r28(b.y)} ${p2} ${p3}` });
39791
+ }
39792
+ function teeBar(pts, cls) {
39793
+ if (pts.length < 2) return "";
39794
+ const a = pts[pts.length - 2], b = pts[pts.length - 1];
39795
+ const ang = Math.atan2(b.y - a.y, b.x - a.x) + Math.PI / 2;
39796
+ const half = 7;
39797
+ return line({ class: cls, x1: r28(b.x - half * Math.cos(ang)), y1: r28(b.y - half * Math.sin(ang)), x2: r28(b.x + half * Math.cos(ang)), y2: r28(b.y + half * Math.sin(ang)) });
39798
+ }
39799
+ function renderMove(mv, ctx) {
39800
+ const pts = mv.points.map((p) => ({ x: ctx.X(p.x), y: ctx.Y(p.y) }));
39801
+ const strokeCls = mv.kind === "motion" ? "sx-pb-motion" : mv.kind === "shot" ? "sx-pb-shot" : mv.style === "dashed" ? "sx-pb-move-dash" : "sx-pb-move";
39802
+ const headCls = mv.kind === "motion" ? "sx-pb-motion-fill" : mv.kind === "shot" ? "sx-pb-shot-fill" : "sx-pb-move-fill";
39803
+ const parts = [];
39804
+ if (mv.style === "wavy") {
39805
+ parts.push(path({ class: strokeCls, d: wavyPath(pts) }));
39806
+ } else {
39807
+ parts.push(path({ class: strokeCls, d: pts.map((p, i) => `${i === 0 ? "M" : "L"} ${r28(p.x)} ${r28(p.y)}`).join(" ") }));
39808
+ }
39809
+ if (mv.end === "arrow") parts.push(arrowHead4(pts, headCls));
39810
+ else if (mv.end === "tee") parts.push(teeBar(pts, strokeCls));
39811
+ return group({ class: "sx-pb-move-g", "data-kind": mv.kind, "data-player": mv.player }, parts);
39812
+ }
39813
+ function playerSymbol(p, ctx) {
39814
+ const cx = ctx.X(p.x), cy = ctx.Y(p.y), r6 = 10;
39815
+ const parts = [];
39816
+ if (p.side === "defense" || p.pos === "x") {
39817
+ const k = r6 * 0.78;
39818
+ parts.push(line({ class: "sx-pb-x", x1: r28(cx - k), y1: r28(cy - k), x2: r28(cx + k), y2: r28(cy + k) }));
39819
+ parts.push(line({ class: "sx-pb-x", x1: r28(cx - k), y1: r28(cy + k), x2: r28(cx + k), y2: r28(cy - k) }));
39820
+ if (p.label) parts.push(text({ class: "sx-pb-x-text", x: r28(cx + k + 5), y: r28(cy - k + 2), "text-anchor": "middle" }, p.label));
39821
+ } else if (p.pos === "gk") {
39822
+ const h = r6 * 1.15;
39823
+ parts.push(polygon({ class: "sx-pb-gk", points: `${r28(cx)},${r28(cy - h)} ${r28(cx + h)},${r28(cy + h * 0.8)} ${r28(cx - h)},${r28(cy + h * 0.8)}` }));
39824
+ parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 6), "text-anchor": "middle" }, p.label));
39825
+ } else if (p.pos === "c") {
39826
+ parts.push(rect({ class: "sx-pb-o", x: r28(cx - r6 * 0.82), y: r28(cy - r6 * 0.82), width: r28(r6 * 1.64), height: r28(r6 * 1.64) }));
39827
+ parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 3.6), "text-anchor": "middle" }, p.label));
39828
+ } else {
39829
+ parts.push(circle({ class: "sx-pb-o", cx: r28(cx), cy: r28(cy), r: r6 }));
39830
+ parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 3.6), "text-anchor": "middle" }, p.label));
39831
+ }
39832
+ return group({ class: "sx-pb-player", "data-side": p.side, "data-id": p.id }, parts);
39833
+ }
39834
+ function legendSwatch(kind, sport) {
39835
+ const moveCls = (dashed) => dashed ? "sx-pb-move-dash" : "sx-pb-move";
39836
+ const arrow2 = polygon({ class: "sx-pb-move-fill", points: "20,0 13,-3.5 13,3.5" });
39837
+ switch (kind) {
39838
+ case "offense":
39839
+ return circle({ class: "sx-pb-o", cx: 8, cy: 0, r: 6 });
39840
+ case "gk":
39841
+ return polygon({ class: "sx-pb-gk", points: "8,-7 15,5 1,5" });
39842
+ case "defense":
39843
+ return `${line({ class: "sx-pb-x", x1: 3, y1: -5, x2: 13, y2: 5 })}${line({ class: "sx-pb-x", x1: 3, y1: 5, x2: 13, y2: -5 })}`;
39844
+ case "run":
39845
+ return `${line({ class: moveCls(sport === "soccer"), x1: 0, y1: 0, x2: 14, y2: 0 })}${arrow2}`;
39846
+ case "pass":
39847
+ return `${line({ class: moveCls(sport !== "soccer"), x1: 0, y1: 0, x2: 14, y2: 0 })}${arrow2}`;
39848
+ case "dribble":
39849
+ return `${path({ class: "sx-pb-move", d: "M0,0 Q3,-4 6,0 T12,0" })}${polygon({ class: "sx-pb-move-fill", points: "20,0 13,-3.5 13,3.5" })}`;
39850
+ case "screen":
39851
+ return `${line({ class: "sx-pb-move", x1: 0, y1: 0, x2: 16, y2: 0 })}${line({ class: "sx-pb-move", x1: 16, y1: -5, x2: 16, y2: 5 })}`;
39852
+ case "shot":
39853
+ return `${line({ class: "sx-pb-shot", x1: 0, y1: 0, x2: 14, y2: 0 })}${polygon({ class: "sx-pb-shot-fill", points: "20,0 13,-3.5 13,3.5" })}`;
39854
+ case "motion":
39855
+ return `${line({ class: "sx-pb-motion", x1: 0, y1: 0, x2: 14, y2: 0 })}${polygon({ class: "sx-pb-motion-fill", points: "20,0 13,-3.5 13,3.5" })}`;
39856
+ case "zone":
39857
+ return rect({ class: "sx-pb-zone", x: 0, y: -6, width: 18, height: 12, rx: 6 });
39858
+ default:
39859
+ return "";
39860
+ }
39861
+ }
39862
+ function renderLegend4(items, y, sport) {
39863
+ const parts = [];
39864
+ let cx = 12;
39865
+ for (const it of items) {
39866
+ parts.push(group({ transform: `translate(${r28(cx)},${r28(y)})` }, [legendSwatch(it.kind, sport)]));
39867
+ parts.push(text({ class: "sx-pb-legend", x: r28(cx + 26), y: r28(y + 4) }, it.label));
39868
+ cx += 26 + it.label.length * 6.5 + 20;
39869
+ }
39870
+ return group({ class: "sx-pb-legend-g" }, parts);
39871
+ }
39872
+ function ordinal(n) {
39873
+ return n === 1 ? "1st" : n === 2 ? "2nd" : n === 3 ? "3rd" : n === 4 ? "4th" : `${n}th`;
39874
+ }
39875
+ function renderPlaybookLayout(lay, config) {
39876
+ let themeName = config?.theme ?? "default";
39877
+ if (lay.sport === "soccer" && themeName === "dark") themeName = "default";
39878
+ const t = resolvePlaybookTheme(themeName);
39879
+ if (lay.errors.length > 0) return renderErrorPanel2(lay, t);
39880
+ const mod = sportModule(lay.sport);
39881
+ const scale = mod.scale;
39882
+ const b = lay.bounds;
39883
+ const titleH = TITLE.bandH;
39884
+ const annoH = lay.sport === "football" && (lay.down || lay.distance || lay.losYard !== void 0) ? 20 : 0;
39885
+ const topH = titleH + annoH;
39886
+ const legendH = 30;
39887
+ const EDGE = 16;
39888
+ const fieldW = (b.maxX - b.minX) * scale;
39889
+ const fieldH = (b.maxY - b.minY) * scale;
39890
+ const W2 = r28(fieldW + EDGE * 2);
39891
+ const fieldTop = topH + EDGE;
39892
+ const H2 = r28(fieldH + topH + EDGE * 2 + legendH);
39893
+ const X = (u) => r28((u - b.minX) * scale + EDGE);
39894
+ const Y = (v) => r28(mod.yUp ? (b.maxY - v) * scale + fieldTop : (v - b.minY) * scale + fieldTop);
39895
+ const px = (u) => r28(u * scale);
39896
+ const ctx = { X, Y, px };
39897
+ const isCourt = lay.sport === "basketball";
39898
+ const surfaceCls = isCourt ? "sx-pb-court" : lay.sport === "soccer" ? "sx-pb-turf" : "sx-pb-field";
39899
+ const surroundCls = isCourt ? "sx-pb-surround-court" : "sx-pb-surround";
39900
+ const boundaryCls = isCourt ? "sx-pb-boundary-court" : "sx-pb-boundary";
39901
+ const fieldRx = 7;
39902
+ const surround = rect({ class: surroundCls, x: 2, y: r28(topH), width: r28(W2 - 4), height: r28(fieldH + EDGE * 2), rx: 12 });
39903
+ const surfaceBase = rect({ class: surfaceCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
39904
+ const boundary = rect({ class: boundaryCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
39905
+ const clipId = "sx-pb-clip";
39906
+ const clip6 = el("clipPath", { id: clipId }, [rect({ x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx })]);
39907
+ const field = group({ "clip-path": `url(#${clipId})` }, [mod.drawField(lay, ctx, t)]);
39908
+ const zones = [];
39909
+ for (const z of lay.zones) {
39910
+ zones.push(el("ellipse", { class: "sx-pb-zone", cx: X(z.x), cy: Y(z.y), rx: px(z.rx), ry: px(z.ry) }));
39911
+ if (z.label) zones.push(text({ class: "sx-pb-zone-text", x: X(z.x), y: r28(Y(z.y) - px(z.ry) + (mod.yUp ? 11 : -4)), "text-anchor": "middle" }, z.label));
39912
+ }
39913
+ const moves = lay.moves.map((m) => renderMove(m, ctx));
39914
+ const players = lay.players.map((p) => playerSymbol(p, ctx));
39915
+ const ball = lay.sport === "football" ? el("ellipse", { class: "sx-pb-ball", cx: X(0), cy: Y(0), rx: 4.5, ry: 2.7 }) : "";
39916
+ const annoParts = [];
39917
+ if (annoH) {
39918
+ const bits = [];
39919
+ if (lay.down) bits.push(ordinal(lay.down) + (lay.distance ? ` & ${lay.distance}` : ""));
39920
+ else if (lay.distance) bits.push(`${lay.distance} to go`);
39921
+ if (lay.losYard !== void 0) bits.push(`ball on ${lay.losYard}`);
39922
+ annoParts.push(text({ class: "sx-pb-anno", x: 8, y: titleH + 14 }, bits.join(" \xB7 ")));
39923
+ }
39924
+ const nOff = lay.players.filter((p) => p.side === "offense").length;
39925
+ const nDef = lay.players.filter((p) => p.side === "defense").length;
39926
+ const descText = `${lay.sport} play. ${nOff} ${lay.sport === "football" ? "offensive players" : "players"}, ${nDef} ${lay.sport === "football" ? "defenders" : "opponents"}, ${lay.moves.length} assignment${lay.moves.length === 1 ? "" : "s"}.` + (lay.warnings.length ? ` Warnings: ${lay.warnings.join("; ")}.` : "");
39927
+ return svgRoot(
39928
+ { viewBox: `0 0 ${W2} ${H2}`, width: W2, height: H2, class: "sx-pb", role: "img" },
39929
+ [
39930
+ title(lay.title),
39931
+ desc(descText),
39932
+ el("style", {}, buildCss14(t)),
39933
+ rect({ fill: t.bg, x: 0, y: 0, width: W2, height: H2 }),
39934
+ el("defs", {}, [clip6]),
39935
+ text({ class: "sx-pb-title", x: r28(W2 / 2), y: TITLE.y, "text-anchor": "middle" }, lay.title),
39936
+ ...annoParts,
39937
+ surround,
39938
+ surfaceBase,
39939
+ field,
39940
+ group({ class: "sx-pb-zones", "clip-path": `url(#${clipId})` }, zones),
39941
+ group({ class: "sx-pb-moves" }, moves),
39942
+ boundary,
39943
+ group({ class: "sx-pb-players" }, players),
39944
+ ball,
39945
+ renderLegend4(mod.legend(lay), fieldH + topH + EDGE * 2 + 18, lay.sport)
39946
+ ]
39947
+ );
39948
+ }
39949
+ function renderPlaybook(text2, config) {
39950
+ return renderPlaybookLayout(layoutPlaybook(parsePlaybook(text2)), config);
39951
+ }
39952
+
39953
+ // src/diagrams/playbook/index.ts
39954
+ var playbook = {
39955
+ type: "playbook",
39956
+ detect(text2) {
39957
+ for (const raw of text2.split(/\r?\n/)) {
39958
+ const t = raw.trim();
39959
+ if (!t) continue;
39960
+ if (t.startsWith("#") || t.startsWith("//")) continue;
39961
+ return /^playbook\b/i.test(t);
39962
+ }
39963
+ return false;
39964
+ },
39965
+ parse: parsePlaybook,
39966
+ render(text2, config) {
39967
+ return renderPlaybook(text2, config);
39968
+ },
39969
+ lint(text2) {
39970
+ try {
39971
+ const lay = layoutPlaybook(parsePlaybook(text2));
39972
+ return [
39973
+ ...lay.errors.map(
39974
+ (message) => ({
39975
+ severity: "error",
39976
+ code: "playbook/validation",
39977
+ message,
39978
+ fatal: false
39979
+ })
39980
+ ),
39981
+ ...lay.warnings.map(
39982
+ (message) => ({
39983
+ severity: "warning",
39984
+ code: "playbook/warning",
39985
+ message,
39986
+ fatal: false
39987
+ })
39988
+ )
39989
+ ];
39990
+ } catch {
39991
+ return [];
39992
+ }
39993
+ }
39994
+ };
39995
+
38476
39996
  // src/core/api.ts
38477
39997
  var plugins = [
38478
39998
  genogram,
@@ -38520,7 +40040,8 @@ var plugins = [
38520
40040
  idef0,
38521
40041
  threatmodel,
38522
40042
  welding,
38523
- floorplan
40043
+ floorplan,
40044
+ playbook
38524
40045
  ];
38525
40046
  function detectPlugin(text2, config) {
38526
40047
  if (config?.type) {
@@ -38531,7 +40052,7 @@ function detectPlugin(text2, config) {
38531
40052
  if (plugin.detect(text2)) return plugin;
38532
40053
  }
38533
40054
  throw new Error(
38534
- "Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', 'bowtie', or 'floorplan'."
40055
+ "Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', 'bowtie', 'floorplan', or 'playbook'."
38535
40056
  );
38536
40057
  }
38537
40058
  function stripCodeFences(text2) {
@@ -38669,5 +40190,5 @@ function renderWithPlugin(prepared, plugin, config) {
38669
40190
  }
38670
40191
 
38671
40192
  export { FLOORPLAN_SYMBOLS, GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
38672
- //# sourceMappingURL=chunk-BP6MLXJU.js.map
38673
- //# sourceMappingURL=chunk-BP6MLXJU.js.map
40193
+ //# sourceMappingURL=chunk-UFXDAIDD.js.map
40194
+ //# sourceMappingURL=chunk-UFXDAIDD.js.map