explico 0.12.0 → 0.13.1

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 (279) hide show
  1. package/CHANGELOG.md +1137 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +4 -3
  4. package/dist/compiler/astro.js +45 -15
  5. package/dist/compiler/cli.d.ts +26 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +9 -1
  8. package/dist/compiler/collections.js +55 -16
  9. package/dist/compiler/content-schema.d.ts +31 -0
  10. package/dist/compiler/content-schema.js +82 -0
  11. package/dist/compiler/content-schemas.d.ts +422 -0
  12. package/dist/compiler/content-schemas.js +229 -0
  13. package/dist/compiler/course-config.d.ts +1 -1
  14. package/dist/compiler/course-config.js +30 -2
  15. package/dist/compiler/exports/combined-html.d.ts +97 -0
  16. package/dist/compiler/exports/combined-html.js +492 -0
  17. package/dist/compiler/exports/download-index.d.ts +9 -0
  18. package/dist/compiler/exports/download-index.js +33 -0
  19. package/dist/compiler/exports/export-chapters.d.ts +98 -0
  20. package/dist/compiler/exports/export-chapters.js +164 -0
  21. package/dist/compiler/exports/export-pages.d.ts +48 -0
  22. package/dist/compiler/exports/export-pages.js +22 -0
  23. package/dist/compiler/exports/html-tree.d.ts +22 -0
  24. package/dist/compiler/exports/html-tree.js +47 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +106 -0
  26. package/dist/compiler/exports/latex/capture.js +435 -0
  27. package/dist/compiler/exports/latex/compile.d.ts +73 -0
  28. package/dist/compiler/exports/latex/compile.js +115 -0
  29. package/dist/compiler/exports/latex/document.d.ts +102 -0
  30. package/dist/compiler/exports/latex/document.js +349 -0
  31. package/dist/compiler/exports/latex/explico.cls +415 -0
  32. package/dist/compiler/exports/latex/numbering.d.ts +18 -0
  33. package/dist/compiler/exports/latex/numbering.js +57 -0
  34. package/dist/compiler/exports/latex/serialize.d.ts +112 -0
  35. package/dist/compiler/exports/latex/serialize.js +606 -0
  36. package/dist/compiler/exports/latex-export.d.ts +44 -0
  37. package/dist/compiler/exports/latex-export.js +169 -0
  38. package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
  39. package/dist/compiler/exports/markdown/serialize.js +527 -0
  40. package/dist/compiler/exports/markdown-export.d.ts +39 -0
  41. package/dist/compiler/exports/markdown-export.js +240 -0
  42. package/dist/compiler/exports/standalone-html.d.ts +14 -5
  43. package/dist/compiler/exports/standalone-html.js +23 -51
  44. package/dist/compiler/exports/static-tree.d.ts +253 -0
  45. package/dist/compiler/exports/static-tree.js +625 -0
  46. package/dist/compiler/exports/zip.d.ts +16 -0
  47. package/dist/compiler/exports/zip.js +72 -0
  48. package/dist/compiler/lesson-derivation.d.ts +8 -2
  49. package/dist/compiler/lesson-derivation.js +6 -7
  50. package/dist/compiler/manifest.d.ts +87 -6
  51. package/dist/compiler/manifest.js +174 -31
  52. package/dist/compiler/render-diagnostics.d.ts +65 -0
  53. package/dist/compiler/render-diagnostics.js +56 -0
  54. package/dist/compiler/resolution-report.d.ts +2 -1
  55. package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
  56. package/dist/curriculum/validation.d.ts +9 -3
  57. package/dist/curriculum/validation.js +35 -17
  58. package/dist/reference/base-abbreviations.d.mts +2 -2
  59. package/dist/reference/base-abbreviations.mjs +2 -2
  60. package/dist/reference/base-operators.d.mts +2 -2
  61. package/dist/reference/base-operators.mjs +2 -2
  62. package/dist/reference/base-roman-labels.d.mts +4 -3
  63. package/dist/reference/base-roman-labels.mjs +4 -3
  64. package/dist/reference/citation-format.d.mts +35 -0
  65. package/dist/reference/citation-format.mjs +74 -0
  66. package/dist/reference/consistency.d.ts +5 -9
  67. package/dist/reference/consistency.js +34 -38
  68. package/dist/reference/curriculum-alignment.js +3 -24
  69. package/dist/reference/derived-entries.d.ts +45 -0
  70. package/dist/reference/derived-entries.js +50 -0
  71. package/dist/reference/derived-notation.d.mts +125 -0
  72. package/dist/reference/derived-notation.mjs +121 -0
  73. package/dist/reference/diagram-fit.d.ts +45 -0
  74. package/dist/reference/diagram-fit.js +54 -0
  75. package/dist/reference/diagram-layout.d.ts +59 -10
  76. package/dist/reference/diagram-layout.js +85 -24
  77. package/dist/reference/diagrams-validate.d.ts +1 -1
  78. package/dist/reference/equations-validate.d.ts +1 -1
  79. package/dist/reference/equations-validate.js +26 -1
  80. package/dist/reference/equations.d.mts +27 -0
  81. package/dist/reference/equations.mjs +120 -22
  82. package/dist/reference/examples-validate.d.ts +1 -1
  83. package/dist/reference/figure-fit.d.ts +70 -0
  84. package/dist/reference/figure-fit.js +60 -0
  85. package/dist/reference/figure-pictures.d.ts +27 -0
  86. package/dist/reference/figure-pictures.js +49 -0
  87. package/dist/reference/figures-validate.d.ts +1 -1
  88. package/dist/reference/gate-abbreviations.d.ts +1 -1
  89. package/dist/reference/gate-abbreviations.js +1 -1
  90. package/dist/reference/gate-course-rules.d.ts +4 -3
  91. package/dist/reference/gate-course-rules.js +9 -4
  92. package/dist/reference/gate-heading-length.d.ts +2 -0
  93. package/dist/reference/gate-heading-length.js +7 -5
  94. package/dist/reference/gate-heading-structure.js +26 -0
  95. package/dist/reference/gate-indices.d.ts +35 -0
  96. package/dist/reference/gate-indices.js +299 -0
  97. package/dist/reference/gate-math.d.ts +32 -2
  98. package/dist/reference/gate-math.js +68 -12
  99. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  100. package/dist/reference/gate-mdx-expression.js +29 -0
  101. package/dist/reference/gate-part-pages.d.ts +1 -1
  102. package/dist/reference/gate-prop-text.d.ts +28 -0
  103. package/dist/reference/gate-prop-text.js +106 -0
  104. package/dist/reference/gate-prose-notation.d.ts +3 -1
  105. package/dist/reference/gate-prose-notation.js +4 -2
  106. package/dist/reference/gate-retired-keys.d.ts +35 -0
  107. package/dist/reference/gate-retired-keys.js +58 -0
  108. package/dist/reference/gloss.d.ts +2 -0
  109. package/dist/reference/katex-measure.d.ts +17 -0
  110. package/dist/reference/katex-measure.js +223 -0
  111. package/dist/reference/keyed-float-validate.js +12 -0
  112. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  113. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  114. package/dist/reference/lab-math-scope.d.ts +1 -0
  115. package/dist/reference/lab-math-scope.js +13 -2
  116. package/dist/reference/math-glyphs.d.mts +94 -2
  117. package/dist/reference/math-glyphs.mjs +851 -46
  118. package/dist/reference/mixed-content.d.ts +8 -3
  119. package/dist/reference/mixed-content.js +4 -2
  120. package/dist/reference/page-notation.d.ts +34 -4
  121. package/dist/reference/page-notation.js +54 -2
  122. package/dist/reference/references.d.ts +11 -1
  123. package/dist/reference/references.js +6 -0
  124. package/dist/reference/registry.js +104 -10
  125. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  126. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  127. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  128. package/dist/reference/remark-citation.mjs +0 -0
  129. package/dist/reference/remark-notation.mjs +84 -20
  130. package/dist/reference/render-lab-math.d.ts +2 -0
  131. package/dist/reference/render-lab-math.js +17 -5
  132. package/dist/reference/sections-validate.d.ts +28 -3
  133. package/dist/reference/sections-validate.js +31 -4
  134. package/dist/reference/sections.d.mts +24 -6
  135. package/dist/reference/sections.mjs +92 -17
  136. package/dist/reference/tables-validate.d.ts +1 -1
  137. package/dist/reference/tables-validate.js +24 -1
  138. package/dist/reference/text-measure.d.ts +10 -0
  139. package/dist/reference/text-measure.js +41 -0
  140. package/dist/reference/text-widths.d.ts +10 -0
  141. package/dist/reference/text-widths.js +1238 -0
  142. package/dist/reference/types.d.ts +64 -8
  143. package/dist/rules/catalog.js +191 -30
  144. package/dist/rules/in-effect.d.ts +1 -1
  145. package/dist/rules/rules.schema.json +79 -24
  146. package/dist/rules/settings.d.ts +1 -1
  147. package/dist/rules/settings.js +7 -0
  148. package/dist/session/PreferenceStore.d.ts +18 -1
  149. package/dist/session/PreferenceStore.js +29 -1
  150. package/docs/authoring/README.md +32 -21
  151. package/docs/authoring/agents.md +38 -7
  152. package/docs/authoring/components.md +111 -6
  153. package/docs/authoring/migrating.md +707 -0
  154. package/docs/authoring/notation.md +281 -29
  155. package/docs/authoring/retired.md +1 -0
  156. package/docs/authoring/structure-rules.md +10 -41
  157. package/docs/authoring/structure.md +53 -2
  158. package/docs/authoring/visualizations.md +144 -18
  159. package/package.json +12 -7
  160. package/src/compiler/astro.ts +57 -17
  161. package/src/compiler/cli.ts +143 -35
  162. package/src/compiler/collections.ts +92 -19
  163. package/src/compiler/content-schema.ts +108 -0
  164. package/src/compiler/content-schemas.ts +391 -0
  165. package/src/compiler/course-config.ts +86 -15
  166. package/src/compiler/exports/combined-html.ts +669 -0
  167. package/src/compiler/exports/download-index.ts +36 -0
  168. package/src/compiler/exports/export-chapters.ts +233 -0
  169. package/src/compiler/exports/export-pages.ts +44 -0
  170. package/src/compiler/exports/html-tree.ts +62 -0
  171. package/src/compiler/exports/latex/capture.ts +651 -0
  172. package/src/compiler/exports/latex/compile.ts +168 -0
  173. package/src/compiler/exports/latex/document.ts +479 -0
  174. package/src/compiler/exports/latex/explico.cls +415 -0
  175. package/src/compiler/exports/latex/numbering.ts +74 -0
  176. package/src/compiler/exports/latex/serialize.ts +787 -0
  177. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  178. package/src/compiler/exports/latex-export.ts +255 -0
  179. package/src/compiler/exports/markdown/serialize.ts +632 -0
  180. package/src/compiler/exports/markdown-export.ts +329 -0
  181. package/src/compiler/exports/standalone-html.ts +39 -70
  182. package/src/compiler/exports/static-tree.ts +944 -0
  183. package/src/compiler/exports/zip.ts +88 -0
  184. package/src/compiler/lesson-derivation.ts +13 -8
  185. package/src/compiler/manifest.ts +299 -28
  186. package/src/compiler/render-diagnostics.ts +111 -0
  187. package/src/components/HoverPanel.astro +204 -6
  188. package/src/components/PartOverview.astro +12 -9
  189. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  190. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  191. package/src/components/assessments/AssessmentSet.astro +14 -1
  192. package/src/components/block-heading.ts +38 -0
  193. package/src/components/card-arrow.ts +18 -4
  194. package/src/components/card-carry.ts +406 -0
  195. package/src/components/card-drag.ts +1 -30
  196. package/src/components/card-fetch.ts +87 -0
  197. package/src/components/card-manager.ts +1222 -38
  198. package/src/components/card-memory.ts +5 -1
  199. package/src/components/card-placement.ts +211 -13
  200. package/src/components/card-side.ts +281 -31
  201. package/src/components/citation/CitationLayer.astro +45 -40
  202. package/src/components/diagram/Diagram.astro +123 -44
  203. package/src/components/figure/Figure.astro +11 -0
  204. package/src/components/figure/FloatCaption.astro +4 -17
  205. package/src/components/float-toolbar.ts +274 -0
  206. package/src/components/memo/Memo.astro +21 -10
  207. package/src/components/memo/ShortNote.astro +4 -0
  208. package/src/components/notation/NotationGlossary.astro +22 -17
  209. package/src/components/notation/NotationLayer.astro +321 -79
  210. package/src/components/notation/NotationMath.astro +8 -1
  211. package/src/components/notation/render-notation-math.ts +51 -5
  212. package/src/components/readable-text.ts +25 -0
  213. package/src/components/reference/FloatRefLayer.astro +162 -89
  214. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  215. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  216. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  217. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  218. package/src/components/starlight/LessonExportActions.astro +384 -310
  219. package/src/components/starlight/LessonFooter.astro +47 -9
  220. package/src/components/starlight/PrintController.astro +28 -0
  221. package/src/components/starlight/TextSizeControl.astro +137 -0
  222. package/src/components/starlight/print-controller.ts +380 -0
  223. package/src/components/starlight/section-summary.ts +13 -5
  224. package/src/components/table/Table.astro +13 -21
  225. package/src/components/table/table-truncation.ts +15 -0
  226. package/src/content-config.ts +20 -312
  227. package/src/curriculum/validation.ts +72 -24
  228. package/src/reference/base-abbreviations.mjs +2 -2
  229. package/src/reference/base-operators.mjs +2 -2
  230. package/src/reference/base-roman-labels.mjs +4 -3
  231. package/src/reference/citation-format.mjs +74 -0
  232. package/src/reference/consistency.ts +47 -61
  233. package/src/reference/curriculum-alignment.ts +4 -41
  234. package/src/reference/derived-entries.ts +68 -0
  235. package/src/reference/derived-notation.mjs +121 -0
  236. package/src/reference/diagram-fit.ts +104 -0
  237. package/src/reference/diagram-layout.ts +182 -51
  238. package/src/reference/diagrams-validate.ts +2 -1
  239. package/src/reference/equations-validate.ts +41 -2
  240. package/src/reference/equations.mjs +120 -22
  241. package/src/reference/examples-validate.ts +1 -0
  242. package/src/reference/figure-fit.ts +139 -0
  243. package/src/reference/figure-pictures.ts +96 -0
  244. package/src/reference/figures-validate.ts +2 -1
  245. package/src/reference/gate-abbreviations.ts +1 -1
  246. package/src/reference/gate-course-rules.ts +22 -7
  247. package/src/reference/gate-heading-length.ts +25 -7
  248. package/src/reference/gate-heading-structure.ts +40 -1
  249. package/src/reference/gate-indices.ts +470 -0
  250. package/src/reference/gate-math.ts +102 -13
  251. package/src/reference/gate-mdx-expression.ts +45 -0
  252. package/src/reference/gate-prop-text.ts +145 -0
  253. package/src/reference/gate-prose-notation.ts +19 -5
  254. package/src/reference/gate-retired-keys.ts +101 -0
  255. package/src/reference/gloss.ts +2 -0
  256. package/src/reference/katex-measure.ts +384 -0
  257. package/src/reference/keyed-float-validate.ts +22 -0
  258. package/src/reference/keyed-jsx-floats.mjs +11 -14
  259. package/src/reference/lab-math-scope.ts +15 -1
  260. package/src/reference/math-glyphs.mjs +851 -46
  261. package/src/reference/mixed-content.ts +11 -2
  262. package/src/reference/page-notation.ts +100 -3
  263. package/src/reference/references.ts +14 -0
  264. package/src/reference/registry.ts +154 -8
  265. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  266. package/src/reference/rehype-heading-anchors.mjs +11 -1
  267. package/src/reference/remark-citation.mjs +0 -0
  268. package/src/reference/remark-notation.mjs +84 -20
  269. package/src/reference/render-lab-math.ts +29 -5
  270. package/src/reference/sections-validate.ts +67 -3
  271. package/src/reference/sections.mjs +92 -17
  272. package/src/reference/tables-validate.ts +40 -2
  273. package/src/reference/text-measure.ts +63 -0
  274. package/src/reference/text-widths.ts +133 -0
  275. package/src/reference/types.ts +75 -8
  276. package/src/rules/catalog.ts +205 -29
  277. package/src/rules/settings.ts +10 -0
  278. package/src/session/PreferenceStore.ts +53 -0
  279. package/src/styles/global.css +291 -75
@@ -0,0 +1,606 @@
1
+ import { captionTitle } from "../../../components/float-name.js";
2
+ function labelOf(route, anchor) {
3
+ return `${route}/${anchor}`;
4
+ }
5
+ const TEXT_ESCAPES = {
6
+ "\\": "\\textbackslash{}",
7
+ "{": "\\{",
8
+ "}": "\\}",
9
+ $: "\\$",
10
+ "&": "\\&",
11
+ "#": "\\#",
12
+ "^": "\\textasciicircum{}",
13
+ _: "\\_",
14
+ "%": "\\%",
15
+ "~": "\\textasciitilde{}",
16
+ "<": "\\textless{}",
17
+ ">": "\\textgreater{}",
18
+ "|": "\\textbar{}",
19
+ "\xA0": "~",
20
+ "\u200B": ""
21
+ };
22
+ const GREEK_WORD = /[\u0370-\u03FF\u1F00-\u1FFF]{2,}/gu;
23
+ function escapeText(text) {
24
+ return text.replace(/[\\{}$&#^_%~<>| ​]/gu, (char) => TEXT_ESCAPES[char] ?? char).replace(GREEK_WORD, (word) => `\\ExplicoGreekWord{${word}}`);
25
+ }
26
+ function escapeUrl(url) {
27
+ return url.replace(/[\\{}]/gu, "").replace(/[%#]/gu, (char) => `\\${char}`);
28
+ }
29
+ function texVisibleLength(tex) {
30
+ return tex.replace(/\\(?:explain|slot)\{[^{}]*\}/gu, "").replace(/\\(?:left|right|[bB]igg?[lr]?|quad|qquad)(?![a-zA-Z])/gu, "").replace(/\\[,;:!]/gu, "").replace(/\\[a-zA-Z]+/gu, "x").replace(/[{}^_\s]/gu, "").length;
31
+ }
32
+ function visibleLength(inlines) {
33
+ let length = 0;
34
+ for (const inline of inlines) {
35
+ if (inline.kind === "text" || inline.kind === "code") length += inline.value.length;
36
+ else if (inline.kind === "math") length += texVisibleLength(inline.tex);
37
+ else if (inline.kind === "cite") length += inline.number.length + 2;
38
+ else if ("children" in inline) length += visibleLength(inline.children);
39
+ }
40
+ return length;
41
+ }
42
+ function isMathOnly(inlines) {
43
+ const content = inlines.filter((inline) => !(inline.kind === "text" && !inline.value.trim()));
44
+ return content.length > 0 && content.every((inline) => inline.kind === "math");
45
+ }
46
+ function plainText(inlines) {
47
+ return inlines.map((inline) => {
48
+ switch (inline.kind) {
49
+ case "text":
50
+ case "code":
51
+ return inline.value;
52
+ case "math":
53
+ return inline.tex;
54
+ case "break":
55
+ return " ";
56
+ case "cite":
57
+ return `[${inline.number}]`;
58
+ case "memoMark":
59
+ return inline.label;
60
+ default:
61
+ return "children" in inline ? plainText(inline.children) : "";
62
+ }
63
+ }).join("");
64
+ }
65
+ function citationKeys(blocks) {
66
+ const keys = /* @__PURE__ */ new Map();
67
+ const visitInlines = (inlines) => {
68
+ for (const inline of inlines) {
69
+ if (inline.kind === "cite" && inline.sourceId) keys.set(inline.anchor, inline.sourceId);
70
+ if ("children" in inline) visitInlines(inline.children);
71
+ }
72
+ };
73
+ const visitBlocks = (list) => {
74
+ for (const block of list) {
75
+ if ("children" in block && Array.isArray(block.children)) {
76
+ if (block.kind === "paragraph" || block.kind === "heading") visitInlines(block.children);
77
+ else visitBlocks(block.children);
78
+ }
79
+ if (block.kind === "list") block.items.forEach(visitBlocks);
80
+ if (block.kind === "examples") block.items.forEach((item) => visitBlocks(item.children));
81
+ if (block.kind === "table") [...block.head, ...block.body].flat().forEach((cell) => visitInlines(cell.children));
82
+ if ("caption" in block) visitInlines(block.caption);
83
+ }
84
+ };
85
+ visitBlocks(blocks);
86
+ return keys;
87
+ }
88
+ function entryKeys(sourceByAnchor) {
89
+ const anchorsBySource = /* @__PURE__ */ new Map();
90
+ for (const [anchor, sourceId] of sourceByAnchor) {
91
+ const anchors = anchorsBySource.get(sourceId) ?? [];
92
+ anchors.push(anchor);
93
+ anchorsBySource.set(sourceId, anchors);
94
+ }
95
+ const keys = /* @__PURE__ */ new Map();
96
+ for (const [sourceId, anchors] of anchorsBySource) {
97
+ for (const anchor of anchors) {
98
+ keys.set(anchor, anchors.length === 1 ? sourceId : `${sourceId}/${anchor}`);
99
+ }
100
+ }
101
+ return keys;
102
+ }
103
+ function linkExplains(tex, linkTo) {
104
+ const marker = "\\explain{";
105
+ let out = "";
106
+ let index = 0;
107
+ for (; ; ) {
108
+ const start = tex.indexOf(marker, index);
109
+ if (start < 0) return out + tex.slice(index);
110
+ const keyEnd = tex.indexOf("}", start + marker.length);
111
+ if (keyEnd < 0 || tex[keyEnd + 1] !== "{") {
112
+ out += tex.slice(index, start + marker.length);
113
+ index = start + marker.length;
114
+ continue;
115
+ }
116
+ let depth = 0;
117
+ let close = keyEnd + 1;
118
+ for (; close < tex.length; close++) {
119
+ const char = tex[close];
120
+ if (char === "\\") close++;
121
+ else if (char === "{") depth++;
122
+ else if (char === "}" && --depth === 0) break;
123
+ }
124
+ if (close >= tex.length) return out + tex.slice(index);
125
+ const key = tex.slice(start + marker.length, keyEnd);
126
+ const target = linkTo(key);
127
+ const inner = tex.slice(keyEnd + 2, close);
128
+ const body = target ? inner : linkExplains(inner, linkTo);
129
+ out += tex.slice(index, start);
130
+ out += target ? `\\ExplicoNotation{${target}}{${body}}` : `\\explain{${key}}{${body}}`;
131
+ index = close + 1;
132
+ }
133
+ }
134
+ class ChapterWriter {
135
+ constructor(context) {
136
+ this.context = context;
137
+ this.route = context.page.route;
138
+ this.citationKeys = entryKeys(citationKeys(context.page.blocks));
139
+ }
140
+ context;
141
+ route;
142
+ citationKeys;
143
+ /** Above zero inside a moving argument (a heading, a caption, a title) or a link: no link goes there. */
144
+ unlinked = 0;
145
+ /** A reference-list entry's citation key in this document, by its anchor. */
146
+ citeKey(anchor) {
147
+ const key = this.citationKeys.get(anchor) ?? anchor;
148
+ return this.context.inBook ? `${this.route}/${key}` : key;
149
+ }
150
+ label(anchor) {
151
+ return labelOf(this.route, anchor);
152
+ }
153
+ /** Renders `write` with links to notation off: a link cannot nest, or sit in a moving argument. */
154
+ unlinkedWhile(write) {
155
+ this.unlinked++;
156
+ try {
157
+ return write();
158
+ } finally {
159
+ this.unlinked--;
160
+ }
161
+ }
162
+ /** Inline content with no notation links inside it. */
163
+ plain(inlines) {
164
+ return this.unlinkedWhile(() => this.inlines(inlines));
165
+ }
166
+ /** A formula's TeX, with its notation markers linked to the appendix where the document has the entry. */
167
+ math(tex) {
168
+ const targets = this.context.notationTargets;
169
+ if (this.unlinked > 0 || !targets || targets.size === 0) return tex;
170
+ return linkExplains(tex, (key) => targets.has(key) ? labelOf("notation", key) : void 0);
171
+ }
172
+ inDocument(target) {
173
+ return this.context.documentRoutes.has(target.route);
174
+ }
175
+ canonicalUrl(target) {
176
+ const base = this.context.canonicalBaseUrl;
177
+ if (!base) return void 0;
178
+ const path = target.route ? `${target.route}/` : "";
179
+ return `${base}${path}${target.anchor ? `#${target.anchor}` : ""}`;
180
+ }
181
+ /** A cross-reference: LaTeX's number inside the document, the website's outside it. */
182
+ reference(refKind, target, children) {
183
+ const text = plainText(children);
184
+ if (this.inDocument(target) && target.anchor) {
185
+ const label = labelOf(target.route, target.anchor);
186
+ if (refKind === "equation" && /^\(\s*[\d.]+\s*\)$/u.test(text.trim())) {
187
+ return `\\ExplicoEqRef{${label}}`;
188
+ }
189
+ const number = /\d+(?:\.\d+)*/u.exec(text);
190
+ if (number && ["equation", "figure", "table", "diagram", "section", "example", "check"].includes(refKind)) {
191
+ const before = text.slice(0, number.index);
192
+ const after = text.slice(number.index + number[0].length);
193
+ return `\\ExplicoRef{${label}}{${escapeText(before)}}{${escapeText(after)}}`;
194
+ }
195
+ return `\\hyperref[${label}]{${this.plain(children)}}`;
196
+ }
197
+ const url = this.canonicalUrl(target);
198
+ return url ? `\\href{${escapeUrl(url)}}{${this.plain(children)}}` : this.inlines(children);
199
+ }
200
+ inline(inline) {
201
+ switch (inline.kind) {
202
+ case "text":
203
+ return escapeText(inline.value);
204
+ case "emphasis":
205
+ return `\\emph{${this.inlines(inline.children)}}`;
206
+ case "strong":
207
+ return `\\textbf{${this.inlines(inline.children)}}`;
208
+ case "superscript":
209
+ return `\\textsuperscript{${this.inlines(inline.children)}}`;
210
+ case "subscript":
211
+ return `\\textsubscript{${this.inlines(inline.children)}}`;
212
+ case "code":
213
+ return `\\texttt{${escapeText(inline.value)}}`;
214
+ case "math":
215
+ return `$${this.math(inline.tex)}$`;
216
+ case "break":
217
+ return "\\newline ";
218
+ case "link":
219
+ return `\\href{${escapeUrl(inline.href)}}{${this.plain(inline.children)}}`;
220
+ case "ref":
221
+ return this.reference(inline.refKind, inline.target, inline.children);
222
+ case "term":
223
+ return `\\ExplicoTerm{${inline.key}}{${this.inlines(inline.children)}}`;
224
+ case "cite":
225
+ return inline.sourceId ? `\\cite{${this.citeKey(inline.anchor)}}` : `[${escapeText(inline.number)}]`;
226
+ case "memoMark":
227
+ return `\\ExplicoMemoMark{${this.label(inline.anchor)}}`;
228
+ case "noteMark":
229
+ return `\\ExplicoNoteMark{${this.label(inline.anchor)}}{${this.plain(inline.children)}}`;
230
+ }
231
+ }
232
+ inlines(inlines) {
233
+ return inlines.map((inline) => this.inline(inline)).join("");
234
+ }
235
+ segments(segments) {
236
+ return segments.map((segment) => segment.type === "math" ? `$${this.math(segment.latex)}$` : escapeText(segment.value)).join("");
237
+ }
238
+ /** A float's caption: its short caption for the lists, bold before the long one. */
239
+ caption(block, extra = []) {
240
+ const short = block.short?.trim();
241
+ const long = [this.plain(block.caption), this.plain(extra)].filter(Boolean).join(" ");
242
+ const title = short ? `\\ExplicoCaptionTitle{${escapeText(captionTitle(short))}} ` : "";
243
+ const list = short ? `[${escapeText(short)}]` : "";
244
+ return `\\caption${list}{${title}${long}}\\label{${this.label(block.anchor)}}`;
245
+ }
246
+ /**
247
+ * A float. Inside a box (a memo, a worked example) a float cannot float, so
248
+ * it is set where it stands (`ExplicoHere`, explico.cls) and keeps its number.
249
+ */
250
+ float(kind, inPlace, content, caption, captionFirst = false) {
251
+ const parts = captionFirst ? [caption, content] : [content, caption];
252
+ if (inPlace) return [`\\begin{ExplicoHere}{${kind}}`, ...parts, "\\end{ExplicoHere}"].join("\n");
253
+ return [`\\begin{${kind}}[htbp]`, "\\centering", ...parts, `\\end{${kind}}`].join("\n");
254
+ }
255
+ picture(anchor, picture, alt) {
256
+ if (picture.kind === "diagram") return diagramTikz(picture.geometry, this);
257
+ const file = this.context.pictures.get(anchor);
258
+ if (file) {
259
+ const text = escapeText(alt || "Figure");
260
+ return `\\includegraphics[alt={${text}},max width=\\linewidth,max height=0.6\\textheight]{${file}}`;
261
+ }
262
+ return `\\fbox{\\parbox{0.9\\linewidth}{\\centering\\small\\itshape ${escapeText(
263
+ alt || "Interactive figure"
264
+ )}: see the interactive version on the course website.}}`;
265
+ }
266
+ table(block, inBox = false) {
267
+ const rows = [...block.head, ...block.body];
268
+ const columns = Math.max(0, ...rows.map((row2) => row2.reduce((sum, cell) => sum + cell.colspan, 0)));
269
+ if (columns === 0) return "";
270
+ const cellAt = (row2, column) => {
271
+ let position = 0;
272
+ for (const cell of row2) {
273
+ if (position === column && cell.colspan === 1) return cell;
274
+ position += cell.colspan;
275
+ }
276
+ return void 0;
277
+ };
278
+ const widths = Array.from(
279
+ { length: columns },
280
+ (_, column) => Math.max(0, ...rows.map((row2) => visibleLength(cellAt(row2, column)?.children ?? [])))
281
+ );
282
+ const prose = Array.from(
283
+ { length: columns },
284
+ (_, column) => block.body.some((row2) => {
285
+ const cell = cellAt(row2, column);
286
+ return !!cell && !isMathOnly(cell.children) && /[a-z]{3}/iu.test(plainText(cell.children));
287
+ })
288
+ );
289
+ const total = widths.reduce((sum, width) => sum + width + 2, 0);
290
+ const wraps = total > 75 && prose.some((isProse, column) => isProse && widths[column] > 18);
291
+ const alignOf = (column) => {
292
+ if (wraps && prose[column] && widths[column] > 18) return ">{\\raggedright\\arraybackslash}X";
293
+ const align = (block.body[0] && cellAt(block.body[0], column)?.align) ?? cellAt(block.head[0] ?? [], column)?.align;
294
+ return align === "right" ? "r" : align === "center" ? "c" : "l";
295
+ };
296
+ const spec = Array.from({ length: columns }, (_, column) => alignOf(column)).join("");
297
+ const dense = columns >= 8;
298
+ const row = (cells) => `${cells.map((cell) => {
299
+ const content = this.inlines(cell.children);
300
+ return cell.colspan > 1 ? `\\multicolumn{${cell.colspan}}{l}{${content}}` : content;
301
+ }).join(" & ")} \\\\`;
302
+ const body = [
303
+ "\\toprule",
304
+ ...block.head.map(row),
305
+ ...block.head.length > 0 ? ["\\midrule"] : [],
306
+ ...block.body.map(row),
307
+ "\\bottomrule"
308
+ ];
309
+ const tabular = wraps ? ["\\small", `\\begin{tabularx}{\\linewidth}{@{}${spec}@{}}`, ...body, "\\end{tabularx}"].join("\n") : [
310
+ dense ? "\\footnotesize\\setlength{\\tabcolsep}{4pt}" : "\\small",
311
+ "\\begin{adjustbox}{max width=\\linewidth}",
312
+ `\\begin{tabular}{@{}${spec}@{}}`,
313
+ ...body,
314
+ "\\end{tabular}",
315
+ "\\end{adjustbox}"
316
+ ].join("\n");
317
+ if (!block.number) return `\\begin{center}
318
+ ${tabular}
319
+ \\end{center}`;
320
+ return this.float("table", inBox, tabular, this.caption(block), true);
321
+ }
322
+ /** A choice's label, its math typeset like the prompt's. */
323
+ optionLabel(option) {
324
+ return option.labelSegments ? this.segments(option.labelSegments) : escapeText(option.label);
325
+ }
326
+ check(block) {
327
+ const items = this.context.checkItems.get(block.assessmentId) ?? [];
328
+ const questions = items.map((item) => {
329
+ const options = item.type === "single-choice" && item.options && item.options.length > 0 ? [
330
+ "\\begin{enumerate}[label=(\\alph*)]",
331
+ ...item.options.map((option) => `\\item ${this.optionLabel(option)}`),
332
+ "\\end{enumerate}"
333
+ ].join("\n") : "";
334
+ return `\\item ${this.segments(item.promptSegments)}${options ? `
335
+ ${options}` : ""}`;
336
+ });
337
+ return [
338
+ `\\begin{ExplicoCheck}{${this.plain(block.title)}}\\label{${this.label(block.anchor)}}`,
339
+ ...questions.length > 0 ? ["\\begin{enumerate}", ...questions, "\\end{enumerate}"] : [],
340
+ `\\ExplicoCheckAnswerNote{${this.label("answers")}}`,
341
+ "\\end{ExplicoCheck}"
342
+ ].join("\n");
343
+ }
344
+ /** The chapter's answer key: every check's answers and explanations, in order. */
345
+ answers(checks) {
346
+ if (checks.length === 0) return "";
347
+ const parts = [`\\section*{Answers to the knowledge checks}\\hypertarget{${this.label("answers")}}{}`];
348
+ for (const check of checks) {
349
+ const items = this.context.checkItems.get(check.assessmentId) ?? [];
350
+ parts.push(
351
+ `\\paragraph*{\\ExplicoRef{${this.label(check.anchor)}}{Knowledge check~}{}: ${this.plain(check.title)}}`
352
+ );
353
+ parts.push("\\begin{enumerate}");
354
+ for (const item of items) {
355
+ let answer = "";
356
+ if (item.type === "single-choice" && item.options) {
357
+ const index = item.options.findIndex((option) => option.id === item.correctOptionId);
358
+ if (index !== -1) {
359
+ answer = `\\textbf{(${String.fromCharCode(97 + index)})} ${this.optionLabel(item.options[index])}. `;
360
+ }
361
+ } else if (item.type === "numeric" && item.answer) {
362
+ answer = `\\textbf{$${item.answer.value}$} (accepted within $\\pm${item.answer.tolerance}$). `;
363
+ }
364
+ parts.push(`\\item ${answer}${this.segments(item.explanationSegments)}`);
365
+ }
366
+ parts.push("\\end{enumerate}");
367
+ }
368
+ return parts.join("\n");
369
+ }
370
+ block(block, inBox = false) {
371
+ switch (block.kind) {
372
+ case "heading": {
373
+ const text = this.plain(block.children);
374
+ const label = block.anchor ? `\\label{${this.label(block.anchor)}}` : "";
375
+ if (inBox) return `\\par\\smallskip\\noindent\\textbf{${text}}${label}\\par`;
376
+ const command = ["section", "subsection", "subsubsection", "paragraph", "subparagraph"][Math.min(Math.max(block.level - 2, 0), 4)] ?? "paragraph";
377
+ const star = block.number ? "" : "*";
378
+ return `\\${command}${star}{${text}}${label}`;
379
+ }
380
+ case "paragraph":
381
+ return this.inlines(block.children);
382
+ case "list": {
383
+ const environment = block.ordered ? "enumerate" : "itemize";
384
+ const start = block.ordered && block.start && block.start !== 1 ? `\\setcounter{enumi}{${block.start - 1}}
385
+ ` : "";
386
+ const items = block.items.map((item) => `\\item ${this.blocks(item, inBox)}`);
387
+ return `\\begin{${environment}}
388
+ ${start}${items.join("\n")}
389
+ \\end{${environment}}`;
390
+ }
391
+ case "equation":
392
+ return `\\begin{equation}\\label{${this.label(block.anchor)}}
393
+ ${this.math(block.tex)}
394
+ \\end{equation}`;
395
+ case "displayMath":
396
+ return `\\[
397
+ ${this.math(block.tex)}
398
+ \\]`;
399
+ case "figure":
400
+ return this.float(
401
+ "figure",
402
+ inBox,
403
+ this.picture(
404
+ block.anchor,
405
+ block.picture,
406
+ block.alt ?? [block.short && captionTitle(block.short), plainText(block.caption)].filter(Boolean).join(" ")
407
+ ),
408
+ this.caption(block, block.live)
409
+ );
410
+ case "diagram":
411
+ return this.float(
412
+ "diagram",
413
+ inBox,
414
+ this.unlinkedWhile(() => diagramTikz(block.geometry, this)),
415
+ this.caption(block)
416
+ );
417
+ case "table":
418
+ return this.table(block, inBox);
419
+ case "memo":
420
+ return [
421
+ `\\begin{ExplicoMemo}{${this.plain(block.title)}}\\label{${this.label(block.anchor)}}`,
422
+ this.blocks(block.children, true),
423
+ "\\end{ExplicoMemo}"
424
+ ].join("\n");
425
+ case "note":
426
+ return [
427
+ `\\begin{ExplicoNote}{${this.label(block.anchor)}}{${escapeText(block.title)}}`,
428
+ this.blocks(block.children, true),
429
+ "\\end{ExplicoNote}"
430
+ ].join("\n");
431
+ case "aside":
432
+ return [
433
+ `\\begin{ExplicoAside}{${block.variant}}{${this.plain(block.title)}}`,
434
+ this.blocks(block.children, true),
435
+ "\\end{ExplicoAside}"
436
+ ].join("\n");
437
+ case "fold":
438
+ return [
439
+ ...block.title ? [`\\paragraph*{${this.plain(block.title)}}`] : [],
440
+ this.blocks(block.children, inBox)
441
+ ].join("\n");
442
+ case "examples":
443
+ return [
444
+ `\\begin{ExplicoExamples}{${this.plain(block.title)}}\\label{${this.label(block.anchor)}}`,
445
+ ...block.items.flatMap((item) => [
446
+ `\\ExplicoExample{${this.plain(item.title)}}`,
447
+ this.blocks(item.children, true)
448
+ ]),
449
+ "\\end{ExplicoExamples}"
450
+ ].join("\n");
451
+ case "check":
452
+ return this.check(block);
453
+ case "quote":
454
+ return `\\begin{quote}
455
+ ${this.blocks(block.children, inBox)}
456
+ \\end{quote}`;
457
+ case "image": {
458
+ const file = this.context.pictures.get(block.src);
459
+ return file ? `\\begin{center}\\includegraphics[max width=\\linewidth]{${file}}\\end{center}` : "";
460
+ }
461
+ case "code":
462
+ return `\\begin{verbatim}
463
+ ${block.value}
464
+ \\end{verbatim}`;
465
+ case "rule":
466
+ return "\\par\\noindent\\rule{\\linewidth}{0.4pt}\\par";
467
+ case "references":
468
+ return [
469
+ `\\begin{ExplicoReferences}{${block.items.length}}`,
470
+ ...block.items.map(
471
+ (item) => `\\bibitem{${this.citeKey(item.anchor)}} ${this.plain(item.children)}`
472
+ ),
473
+ "\\end{ExplicoReferences}"
474
+ ].join("\n");
475
+ }
476
+ }
477
+ blocks(blocks, inBox = false) {
478
+ return blocks.map((block) => this.block(block, inBox)).filter((text) => text.trim().length > 0).join("\n\n");
479
+ }
480
+ notation(entries, remarks) {
481
+ if (entries.length === 0) return "";
482
+ const items = entries.map((entry) => {
483
+ const symbol = entry.symbolTex ? `$${entry.symbolTex}$` : "";
484
+ const parts = [
485
+ `\\textbf{${this.inlines(entry.label)}}.`,
486
+ this.inlines(entry.summary),
487
+ ...entry.units ? [`\\emph{Units:} ${this.inlines(entry.units)}.`] : [],
488
+ ...entry.formulaTex ? [`\\[${entry.formulaTex}\\]`] : []
489
+ ];
490
+ return `\\item[${symbol}]\\hypertarget{${this.label(entry.anchor)}}{}${parts.join(" ")}`;
491
+ });
492
+ return [
493
+ "\\section*{Notation}",
494
+ ...remarks.map((remark) => `\\noindent\\emph{${this.inlines(remark)}}\\par\\medskip`),
495
+ "\\begin{description}",
496
+ ...items,
497
+ "\\end{description}"
498
+ ].join("\n");
499
+ }
500
+ }
501
+ function diagramTikz(geometry, writer) {
502
+ if (geometry.nodes.length === 0) return "";
503
+ const scale = 0.75 * Math.min(1, 450 / Math.max(1, geometry.width * 0.75));
504
+ const pt = (value) => `${(value * scale).toFixed(1)}pt`;
505
+ const point = (x, y) => `(${pt(x)},${pt(-y)})`;
506
+ const nodes = geometry.nodes.map((node) => {
507
+ const text = node.text ? writer.inlines([{ kind: "text", value: node.text }]) : "";
508
+ const math = node.tex ? `$${node.tex}$` : "";
509
+ const content = [text, math].filter(Boolean).join("\\\\ ");
510
+ const textWidth = Math.max(10, node.width * scale - 8).toFixed(1);
511
+ return `\\node[explico diagram node,minimum width=${pt(node.width)},minimum height=${pt(node.height)},text width=${textWidth}pt] at ${point(node.x + node.width / 2, node.y + node.height / 2)} {${content}};`;
512
+ });
513
+ const edges = geometry.edges.map(
514
+ (edge) => `\\draw[explico diagram edge] ${edge.map(([x, y]) => point(x, y)).join(" -- ")};`
515
+ );
516
+ return ["\\begin{tikzpicture}", ...edges, ...nodes, "\\end{tikzpicture}"].join("\n");
517
+ }
518
+ function serializeChapter(context) {
519
+ const writer = new ChapterWriter(context);
520
+ const { page } = context;
521
+ const checks = [];
522
+ const collect = (blocks) => {
523
+ for (const block of blocks) {
524
+ if (block.kind === "check") checks.push(block);
525
+ if ("children" in block && Array.isArray(block.children) && block.kind !== "paragraph" && block.kind !== "heading") {
526
+ collect(block.children);
527
+ }
528
+ }
529
+ };
530
+ collect(page.blocks);
531
+ const source = context.source;
532
+ const sourceLine = !source ? [] : context.inBook ? source.url ? [`\\ExplicoInteractive{${escapeUrl(source.url)}}`] : [] : [
533
+ `\\ExplicoSource{${escapeText(source.course)}}{${escapeText(source.version ?? "")}}{${source.url ? escapeUrl(source.url) : ""}}`
534
+ ];
535
+ return [
536
+ `\\chapter{${escapeText(page.title)}}\\label{${labelOf(page.route, "_top")}}`,
537
+ "\\ExplicoApplyOffsets",
538
+ ...sourceLine,
539
+ writer.blocks(page.blocks),
540
+ ...context.inBook ? [] : [writer.notation(page.notation, page.notationRemarks)],
541
+ writer.answers(checks),
542
+ ""
543
+ ].filter((part) => part !== void 0).join("\n\n");
544
+ }
545
+ function serializePartOpener(context, part) {
546
+ const writer = new ChapterWriter({ ...context, inBook: true });
547
+ return [
548
+ `\\ExplicoPart{${part.ordinal}}{${escapeText(part.label)}}\\label{${labelOf(context.page.route, "_top")}}`,
549
+ writer.blocks(context.page.blocks),
550
+ ""
551
+ ].join("\n\n");
552
+ }
553
+ function serializeNotationAppendix(entries, context) {
554
+ if (entries.length === 0) return "";
555
+ const writers = /* @__PURE__ */ new Map();
556
+ const writerFor = (page) => {
557
+ let writer = writers.get(page.route);
558
+ if (!writer) {
559
+ writer = new ChapterWriter({ ...context, page, inBook: true });
560
+ writers.set(page.route, writer);
561
+ }
562
+ return writer;
563
+ };
564
+ const targets = context.notationTargets;
565
+ const formula = (key, tex) => targets ? linkExplains(tex, (named) => named !== key && targets.has(named) ? labelOf("notation", named) : void 0) : tex;
566
+ const items = entries.map(({ entry, page, chapter }) => {
567
+ const writer = writerFor(page);
568
+ const parts = [
569
+ `\\textbf{${writer.inlines(entry.label)}}.`,
570
+ writer.inlines(entry.summary),
571
+ ...entry.units ? [`\\emph{Units:} ${writer.inlines(entry.units)}.`] : [],
572
+ ...chapter ? [`\\emph{In Chapter~${chapter} only.}`] : [],
573
+ ...entry.formulaTex ? [`\\[${formula(entry.key, entry.formulaTex)}\\]`] : []
574
+ ];
575
+ const symbol = entry.symbolTex ? `$${entry.symbolTex}$` : "";
576
+ return `\\item[{${symbol}}]\\hypertarget{${labelOf("notation", entry.key)}}{}${parts.join(" ")}`;
577
+ });
578
+ return [
579
+ "\\chapter{Notation}\\label{appendix/notation}",
580
+ "The symbols of this book, in the order they first appear.",
581
+ "\\begin{description}",
582
+ ...items,
583
+ "\\end{description}",
584
+ ""
585
+ ].join("\n");
586
+ }
587
+ function serializeAbbreviationAppendix(abbreviations) {
588
+ if (abbreviations.length === 0) return "";
589
+ return [
590
+ "\\chapter{Abbreviations}\\label{appendix/abbreviations}",
591
+ "\\begin{description}",
592
+ ...[...abbreviations].sort((a, b) => a.abbr.localeCompare(b.abbr)).map((entry) => `\\item[{${escapeText(entry.abbr)}}] ${escapeText(entry.expansion)}`),
593
+ "\\end{description}",
594
+ ""
595
+ ].join("\n");
596
+ }
597
+ export {
598
+ diagramTikz,
599
+ escapeText,
600
+ labelOf,
601
+ linkExplains,
602
+ serializeAbbreviationAppendix,
603
+ serializeChapter,
604
+ serializeNotationAppendix,
605
+ serializePartOpener
606
+ };
@@ -0,0 +1,44 @@
1
+ import { Manifest } from '../manifest.js';
2
+ import { ExportLogger, PreparedChapters } from './export-chapters.js';
3
+ import '../../curriculum/validation.js';
4
+ import '../../reference/consistency.js';
5
+ import '../../course-config-rRtME5UV.js';
6
+ import '../../rules/catalog.js';
7
+ import '../../rules/course-rules.js';
8
+ import '../collections.js';
9
+ import '../../reference/types.js';
10
+ import '../../reference/gate-retired-keys.js';
11
+ import '../../reference/lint-ignore.js';
12
+ import '../../reference/equations-validate.js';
13
+ import '../../reference/tables-validate.js';
14
+ import '../../reference/keyed-float-validate.js';
15
+ import '../../reference/figures-validate.js';
16
+ import '../../reference/diagrams-validate.js';
17
+ import '../../reference/examples-validate.js';
18
+ import '../../reference/folds-validate.js';
19
+ import '../../reference/knowledge-checks-validate.js';
20
+ import '../../reference/raw-floats-validate.js';
21
+ import '../../reference/lab-katex-validate.js';
22
+ import '../../reference/gate-math.js';
23
+ import '../../reference/mixed-content.js';
24
+ import '../../reference/gloss.js';
25
+ import '../../rules/waivers.js';
26
+ import '../knowledge-version.js';
27
+ import '../lesson-derivation.js';
28
+ import './static-tree.js';
29
+ import './html-tree.js';
30
+ import 'parse5';
31
+ import './latex/capture.js';
32
+
33
+ /**
34
+ * Write each chapter's, each part's and the course's LaTeX source zip and PDF
35
+ * under `downloads/`, from the chapters `prepareChapters` read and drew (read
36
+ * here when not given).
37
+ */
38
+ declare function writeLatexExports(directory: URL, manifest: Manifest, options?: {
39
+ readonly logger?: ExportLogger;
40
+ readonly projectRoot?: string;
41
+ readonly prepared?: PreparedChapters;
42
+ }): Promise<void>;
43
+
44
+ export { ExportLogger, writeLatexExports };