@pretextbook/pretext-html 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/assets/preview-html.xsl +16 -0
  2. package/assets/upstream.json +1 -1
  3. package/assets/xsl/extract-dynamic.xsl +54 -3
  4. package/assets/xsl/extract-gdscript.xsl +61 -0
  5. package/assets/xsl/extract-sageplot.xsl +3 -0
  6. package/assets/xsl/localizations/it-IT.xml +37 -41
  7. package/assets/xsl/pretext-assembly.xsl +188 -24
  8. package/assets/xsl/pretext-beamer.xsl +57 -12
  9. package/assets/xsl/pretext-common.xsl +156 -28
  10. package/assets/xsl/pretext-epub.xsl +0 -5
  11. package/assets/xsl/pretext-fo.xsl +110 -48
  12. package/assets/xsl/pretext-html.xsl +147 -54
  13. package/assets/xsl/pretext-jupyter.xsl +1 -0
  14. package/assets/xsl/pretext-latex-classic.xsl +2 -2
  15. package/assets/xsl/pretext-latex-common.xsl +268 -134
  16. package/assets/xsl/pretext-latex.xsl +16 -6
  17. package/assets/xsl/pretext-numbers.xsl +10 -0
  18. package/assets/xsl/pretext-revealjs.xsl +68 -1
  19. package/assets/xsl/pretext-runestone-fitb.xsl +179 -84
  20. package/assets/xsl/pretext-runestone-static.xsl +72 -20
  21. package/assets/xsl/pretext-runestone.xsl +27 -4
  22. package/assets/xsl/pretext-text-utilities.xsl +7 -0
  23. package/assets/xsl/pretext-text.xsl +17 -0
  24. package/assets/xsl/publisher-variables.xsl +39 -0
  25. package/assets/xsl-bundle.json +1 -1
  26. package/dist/cli-BUrNZxov.js +1098 -0
  27. package/dist/cli.js +1 -1
  28. package/dist/html-static.d.ts +2 -2
  29. package/dist/index.browser.js +595 -386
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +35 -30
  32. package/dist/renderer.d.ts +35 -0
  33. package/dist/skeleton.d.ts +41 -0
  34. package/dist/xrefs.d.ts +54 -0
  35. package/package.json +1 -1
  36. package/dist/cli-Bcn4t3B1.js +0 -871
@@ -0,0 +1,1098 @@
1
+ import { readFile as W, writeFile as Z } from "node:fs/promises";
2
+ import * as y from "node:process";
3
+ import * as d from "node:path";
4
+ import { fileURLToPath as Ae } from "node:url";
5
+ import { fromXml as E } from "xast-util-from-xml";
6
+ import { toXml as I } from "xast-util-to-xml";
7
+ import { injectRevealBridge as Le } from "./reveal.js";
8
+ import { injectThemeBridge as Me } from "./theme.js";
9
+ const je = "ptxPreviewBannerDismissed", _e = "About live preview";
10
+ function Q(e) {
11
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
12
+ }
13
+ function Be(e) {
14
+ return [
15
+ '<div id="ptx-preview-banner" class="ptx-preview-banner" role="note">',
16
+ ` <span class="ptx-preview-banner-text">${Q(e)}</span>`,
17
+ ' <button type="button" class="ptx-preview-banner-dismiss" aria-label="Dismiss">&times;</button>',
18
+ "</div>",
19
+ `<button type="button" id="ptx-preview-banner-restore" class="ptx-preview-banner-restore" hidden>${Q(_e)}</button>`
20
+ ].join(`
21
+ `);
22
+ }
23
+ function Oe() {
24
+ return [
25
+ "<style>",
26
+ ".ptx-preview-banner {",
27
+ " display: flex;",
28
+ " align-items: center;",
29
+ " gap: 0.75em;",
30
+ " padding: 0.5em 1em;",
31
+ " font-size: 0.9em;",
32
+ " background: #fff8e1;",
33
+ " color: #6b5300;",
34
+ " border-bottom: 1px solid #f0c36d;",
35
+ "}",
36
+ // The [hidden] content attribute only hides an element via the
37
+ // user-agent stylesheet's `[hidden] { display: none }` rule, and a
38
+ // normal-priority *author* rule always wins over a normal-priority UA
39
+ // rule regardless of selector specificity — so the unconditional
40
+ // `display: flex` above would otherwise keep the banner visible even
41
+ // after previewBannerScript sets `banner.hidden = true`.
42
+ ".ptx-preview-banner[hidden] {",
43
+ " display: none;",
44
+ "}",
45
+ ".ptx-preview-banner-text {",
46
+ " flex: 1 1 auto;",
47
+ "}",
48
+ ".ptx-preview-banner-dismiss {",
49
+ " flex: 0 0 auto;",
50
+ " background: transparent;",
51
+ " border: none;",
52
+ " font-size: 1.2em;",
53
+ " line-height: 1;",
54
+ " cursor: pointer;",
55
+ " color: inherit;",
56
+ " padding: 0 0.25em;",
57
+ "}",
58
+ "html.dark-mode .ptx-preview-banner {",
59
+ " background: #3a2f00;",
60
+ " color: #f2d27a;",
61
+ " border-bottom-color: #6b5300;",
62
+ "}",
63
+ ".ptx-preview-banner-restore {",
64
+ " position: fixed;",
65
+ " right: 0.5em;",
66
+ " bottom: 0.5em;",
67
+ " z-index: 1000;",
68
+ " background: transparent;",
69
+ " border: none;",
70
+ " padding: 0.15em 0.4em;",
71
+ " font-size: 0.75em;",
72
+ " color: inherit;",
73
+ " opacity: 0.45;",
74
+ " text-decoration: underline;",
75
+ " cursor: pointer;",
76
+ "}",
77
+ ".ptx-preview-banner-restore:hover {",
78
+ " opacity: 0.85;",
79
+ "}",
80
+ "</style>"
81
+ ].join(`
82
+ `);
83
+ }
84
+ function Ce() {
85
+ return [
86
+ "<script>",
87
+ "(function () {",
88
+ " var KEY = " + JSON.stringify(je) + ";",
89
+ ' var banner = document.getElementById("ptx-preview-banner");',
90
+ ' var restore = document.getElementById("ptx-preview-banner-restore");',
91
+ " if (!banner || !restore) { return; }",
92
+ // localStorage can throw (disabled cookies/storage, sandboxed iframe);
93
+ // fall back to always showing the banner rather than crashing the page.
94
+ " function isDismissed() {",
95
+ " try { return window.localStorage.getItem(KEY) === '1'; }",
96
+ " catch (e) { return false; }",
97
+ " }",
98
+ " function setDismissed(value) {",
99
+ " try {",
100
+ " if (value) { window.localStorage.setItem(KEY, '1'); }",
101
+ " else { window.localStorage.removeItem(KEY); }",
102
+ " } catch (e) {}",
103
+ " }",
104
+ " function apply(dismissed) {",
105
+ " banner.hidden = dismissed;",
106
+ " restore.hidden = !dismissed;",
107
+ " }",
108
+ " apply(isDismissed());",
109
+ ' var dismissBtn = banner.querySelector(".ptx-preview-banner-dismiss");',
110
+ " if (dismissBtn) {",
111
+ " dismissBtn.addEventListener('click', function () {",
112
+ " setDismissed(true);",
113
+ " apply(true);",
114
+ " });",
115
+ " }",
116
+ " restore.addEventListener('click', function () {",
117
+ " setDismissed(false);",
118
+ " apply(false);",
119
+ " });",
120
+ "})();",
121
+ "<\/script>"
122
+ ].join(`
123
+ `);
124
+ }
125
+ const Fe = "<!--ptx-preview-banner-->", Ne = "<!--/ptx-preview-banner-->", Xe = /\n?<!--ptx-preview-banner-->[\s\S]*?<!--\/ptx-preview-banner-->/;
126
+ function Ue(e, r) {
127
+ const t = Xe.exec(e), n = t ? e.slice(0, t.index) + e.slice(t.index + t[0].length) : e;
128
+ if (!/<body[^>]*>/i.test(n))
129
+ return n;
130
+ const o = `
131
+ ${Fe}
132
+ ${Oe()}
133
+ ${Be(r.message)}
134
+ ${Ce()}
135
+ ${Ne}`;
136
+ return n.replace(/<body([^>]*)>/i, `<body$1>${o}`);
137
+ }
138
+ async function de(e, r) {
139
+ const t = d.resolve(e), n = d.resolve(t, `.${d.posix.normalize(r)}`);
140
+ if (n !== t && !n.startsWith(t + d.sep)) {
141
+ console.error(
142
+ `pretext-html: refusing to serve ${r} from outside ${t}`
143
+ );
144
+ return;
145
+ }
146
+ try {
147
+ return new Uint8Array(await W(n));
148
+ } catch {
149
+ return;
150
+ }
151
+ }
152
+ async function M(e) {
153
+ try {
154
+ return await W(e, "utf8");
155
+ } catch {
156
+ return;
157
+ }
158
+ }
159
+ let fe;
160
+ function en(e) {
161
+ fe = e;
162
+ }
163
+ function C() {
164
+ return fe ?? process.env.PRETEXT_HTML_ASSETS ?? d.resolve(d.dirname(Ae(import.meta.url)), "..", "assets");
165
+ }
166
+ function We(e, r) {
167
+ return d.join(e, r);
168
+ }
169
+ const pe = "2.48.1", He = "8.2.4", Je = [
170
+ "prefix-runtime.40bd1420d3277749.bundle.js",
171
+ "prefix-926.5c7038c8ef173faf.bundle.js",
172
+ "prefix-runestone.b388362b5f43738a.bundle.js"
173
+ ], ze = [
174
+ "prefix-926.229729d09512acc9.css",
175
+ "prefix-runestone.8086b62682b271d1.css"
176
+ ], Ke = "born-hidden-knowl", Ve = /<details\b([^>]*)>(\s*)(?:<summary\b([^>]*)>)?/g;
177
+ function Ge(e) {
178
+ const r = /\bclass="([^"]*)"/.exec(e);
179
+ return r ? r[1].trim().split(/\s+/) : [];
180
+ }
181
+ function Ye(e) {
182
+ return /(^|\s)open(\s|=|$)/.test(e);
183
+ }
184
+ function ee(e) {
185
+ return Ye(e) ? e : ` open${e}`;
186
+ }
187
+ function qe(e) {
188
+ return e.replace(
189
+ Ve,
190
+ (r, t, n, o) => {
191
+ if (!Ge(t).includes(Ke))
192
+ return r;
193
+ const i = `<details${ee(t)}>`;
194
+ return o === void 0 ? `${i}${n}` : `${i}${n}<summary${ee(o)}>`;
195
+ }
196
+ );
197
+ }
198
+ const H = /* @__PURE__ */ new Map(), he = /* @__PURE__ */ new Map();
199
+ let F, Ze = 0, me = de;
200
+ function tn(e) {
201
+ me = e;
202
+ }
203
+ function Qe(e, r) {
204
+ const t = typeof e == "string" ? e : e instanceof URL ? e.href : e.url, n = he.get(t);
205
+ if (n !== void 0)
206
+ return Promise.resolve(new Response(n, { status: 200 }));
207
+ let o;
208
+ try {
209
+ o = new URL(t).host;
210
+ } catch {
211
+ o = "";
212
+ }
213
+ const i = H.get(o);
214
+ return i !== void 0 ? tt(i, t) : F(e, r);
215
+ }
216
+ const et = '<?xml version="1.0"?><pretext-preview-missing-file/>';
217
+ async function tt(e, r) {
218
+ const t = decodeURIComponent(new URL(r).pathname), n = await me(e, t);
219
+ if (n === void 0)
220
+ return new Response(et, { status: 200 });
221
+ const o = typeof n == "string" ? n : new Uint8Array(n);
222
+ return new Response(o, { status: 200 });
223
+ }
224
+ function we() {
225
+ F || (F = globalThis.fetch, globalThis.fetch = Qe);
226
+ }
227
+ function be(e) {
228
+ we();
229
+ const r = `mnt${++Ze}.ptx.invalid`;
230
+ return H.set(r, e), `http://${r}`;
231
+ }
232
+ function nt(e) {
233
+ H.delete(new URL(e).host);
234
+ }
235
+ function rt(e, r) {
236
+ we(), he.set(e, r);
237
+ }
238
+ const N = `<?xml version="1.0" encoding="UTF-8"?>
239
+ <publication>
240
+ <source>
241
+ <directories external="../assets" generated="../generated-assets" />
242
+ </source>
243
+ <html>
244
+ <platform portable="yes"/>
245
+ </html>
246
+ </publication>
247
+ `, ot = ["theme", "style", "shell"];
248
+ function x(e, r) {
249
+ for (const t of e.children)
250
+ if (t.type === "element" && t.name === r)
251
+ return t;
252
+ }
253
+ function T(e, r) {
254
+ let t = x(e, r);
255
+ return t || (t = { type: "element", name: r, attributes: {}, children: [] }, e.children.push(t)), t;
256
+ }
257
+ function it(e, r) {
258
+ const t = x(e, "css");
259
+ if (t && ot.some((o) => t.attributes?.[o]))
260
+ return;
261
+ const n = t ?? T(e, "css");
262
+ n.attributes = { ...n.attributes, theme: r };
263
+ }
264
+ function st(e, r) {
265
+ const t = x(e, "appearance");
266
+ if (t?.attributes?.theme)
267
+ return;
268
+ const n = t ?? T(e, "appearance");
269
+ n.attributes = { ...n.attributes, theme: r };
270
+ }
271
+ function at(e) {
272
+ const r = T(e, "resources");
273
+ r.attributes = {
274
+ ...r.attributes,
275
+ host: "cdn",
276
+ math: "online"
277
+ };
278
+ }
279
+ function ct(e, r = {}) {
280
+ const t = r.cssTheme?.trim(), n = r.revealTheme?.trim(), o = r.target === "slides";
281
+ if (!e && !t && !o)
282
+ return N;
283
+ const i = E(e ?? N), s = x(i, "publication");
284
+ if (!s)
285
+ throw new Error(
286
+ "Invalid publication file: no root <publication> element found"
287
+ );
288
+ const a = T(s, "html"), l = T(a, "platform");
289
+ if (l.attributes = { ...l.attributes, portable: "yes" }, t && it(a, t), o) {
290
+ const c = T(s, "revealjs");
291
+ at(c), n && st(c, n);
292
+ }
293
+ return I(i);
294
+ }
295
+ function te(e) {
296
+ if (!(e === void 0 || e === "" || e.startsWith("/")))
297
+ return e.endsWith("/") ? e : `${e}/`;
298
+ }
299
+ function lt(e) {
300
+ const r = E(e ?? N), t = x(r, "publication"), n = t && x(t, "source"), o = n && x(n, "directories"), i = te(
301
+ o?.attributes?.external ?? void 0
302
+ ), s = te(
303
+ o?.attributes?.generated ?? void 0
304
+ );
305
+ return {
306
+ external: i,
307
+ generated: s,
308
+ managed: i !== void 0 && s !== void 0
309
+ };
310
+ }
311
+ const ge = "http://www.w3.org/2001/XInclude", ne = 64;
312
+ async function ut(e, r, t) {
313
+ return e.includes("include") ? (await J(e, r, t)).content : e;
314
+ }
315
+ async function J(e, r, t) {
316
+ const n = E(e), o = {
317
+ projectDir: d.resolve(t),
318
+ stack: [d.resolve(r)]
319
+ }, i = await P(
320
+ n,
321
+ d.dirname(d.resolve(r)),
322
+ { "": "" },
323
+ o
324
+ );
325
+ return { tree: n, content: i ? I(n) : e };
326
+ }
327
+ function dt(e) {
328
+ return e.data?.ptxIncludedFile;
329
+ }
330
+ function ft(e, r) {
331
+ const t = e.data ?? (e.data = {});
332
+ t.ptxIncludedFile = r;
333
+ }
334
+ function L(e, r) {
335
+ let t = e;
336
+ for (const [n, o] of Object.entries(r.attributes))
337
+ (n === "xmlns" || n.startsWith("xmlns:")) && (t === e && (t = { ...e }), t[n === "xmlns" ? "" : n.slice(6)] = o ?? "");
338
+ return t;
339
+ }
340
+ function ve(e, r) {
341
+ const t = e.name.indexOf(":"), n = t === -1 ? "" : e.name.slice(0, t);
342
+ return (t === -1 ? e.name : e.name.slice(t + 1)) === "include" && r[n] === ge;
343
+ }
344
+ async function P(e, r, t, n) {
345
+ let o = !1;
346
+ for (let i = 0; i < e.children.length; i++) {
347
+ const s = e.children[i];
348
+ if (s.type !== "element")
349
+ continue;
350
+ const a = L(t, s);
351
+ if (ve(s, a)) {
352
+ const l = await xe(
353
+ s,
354
+ r,
355
+ a,
356
+ n
357
+ );
358
+ e.children.splice(i, 1, ...l), i += l.length - 1, o = !0;
359
+ } else await P(s, r, a, n) && (o = !0);
360
+ }
361
+ return o;
362
+ }
363
+ async function xe(e, r, t, n) {
364
+ const o = n.stack[n.stack.length - 1];
365
+ if (e.attributes.xpointer)
366
+ throw new Error(
367
+ `xi:include with xpointer is not supported by the JS preview build (in ${o})`
368
+ );
369
+ const i = e.attributes.href;
370
+ if (!i)
371
+ throw new Error(`xi:include without href (in ${o})`);
372
+ let s = i;
373
+ try {
374
+ s = decodeURIComponent(i);
375
+ } catch {
376
+ }
377
+ const a = d.resolve(r, s);
378
+ if (a !== n.projectDir && !a.startsWith(n.projectDir + d.sep))
379
+ throw new Error(
380
+ `xi:include escapes the project directory: ${i} (in ${o})`
381
+ );
382
+ if (n.stack.includes(a))
383
+ throw new Error(
384
+ `Circular xi:include: ${[...n.stack, a].join(" -> ")}`
385
+ );
386
+ if (n.stack.length >= ne)
387
+ throw new Error(`xi:include nesting exceeds ${ne} levels`);
388
+ const l = await M(a);
389
+ if (l === void 0) {
390
+ const p = ht(e, t);
391
+ if (p)
392
+ return await P(p, r, t, n), p.children;
393
+ throw new Error(`xi:include target not found: ${i} (in ${o})`);
394
+ }
395
+ if (e.attributes.parse === "text")
396
+ return [{ type: "text", value: l }];
397
+ let c;
398
+ try {
399
+ c = E(l);
400
+ } catch (p) {
401
+ throw new Error(
402
+ `Could not parse xi:include target ${a} as XML: ${p instanceof Error ? p.message : p}. (Included files must be well-formed, with a single root element.)`
403
+ );
404
+ }
405
+ n.stack.push(a);
406
+ try {
407
+ await P(c, d.dirname(a), { "": "" }, n);
408
+ } finally {
409
+ n.stack.pop();
410
+ }
411
+ const f = c.children.filter(
412
+ (p) => p.type === "element"
413
+ );
414
+ for (const p of f)
415
+ ft(p, a);
416
+ return f;
417
+ }
418
+ function re(e) {
419
+ return e.name === "docinfo";
420
+ }
421
+ async function pt(e, r, t) {
422
+ try {
423
+ const o = E(e).children.find(
424
+ (c) => c.type === "element"
425
+ );
426
+ if (!o)
427
+ return;
428
+ const i = d.resolve(r), s = d.dirname(i), a = {
429
+ projectDir: d.resolve(t),
430
+ stack: [i]
431
+ }, l = L({ "": "" }, o);
432
+ for (const c of o.children) {
433
+ if (c.type !== "element")
434
+ continue;
435
+ const f = L(l, c);
436
+ if (re(c))
437
+ return await P(c, s, f, a), I(c);
438
+ if (ve(c, f)) {
439
+ const h = (await xe(
440
+ c,
441
+ s,
442
+ f,
443
+ a
444
+ )).find(
445
+ (m) => m.type === "element" && re(m)
446
+ );
447
+ if (h)
448
+ return I(h);
449
+ continue;
450
+ }
451
+ break;
452
+ }
453
+ return;
454
+ } catch {
455
+ return;
456
+ }
457
+ }
458
+ function ht(e, r) {
459
+ for (const t of e.children) {
460
+ if (t.type !== "element")
461
+ continue;
462
+ const n = L(r, t), o = t.name.indexOf(":"), i = o === -1 ? "" : t.name.slice(0, o);
463
+ if ((o === -1 ? t.name : t.name.slice(o + 1)) === "fallback" && n[i] === ge)
464
+ return t;
465
+ }
466
+ }
467
+ function ye(e) {
468
+ return typeof e == "object" && e !== null && e.type === "element";
469
+ }
470
+ function mt(e) {
471
+ return /^(pf|xhtml):/.test(e.name);
472
+ }
473
+ function wt(e, r, t) {
474
+ const n = [], o = e.children.find(ye);
475
+ if (o) {
476
+ const i = t?.parentId ?? "root";
477
+ Te(o, i, t?.position ?? 1, r, i, n);
478
+ }
479
+ return n;
480
+ }
481
+ function Te(e, r, t, n, o, i) {
482
+ const s = dt(e) ?? n;
483
+ let a, l;
484
+ if (mt(e))
485
+ l = "root";
486
+ else {
487
+ const f = e.attributes.label ?? e.attributes["xml:id"];
488
+ a = f ?? `${r}-${t}`, i.push({
489
+ id: a,
490
+ file: s,
491
+ line: e.position?.start.line ?? 1,
492
+ column: e.position?.start.column ?? 1,
493
+ endLine: e.position?.end.line ?? e.position?.start.line ?? 1,
494
+ parent: o
495
+ }), l = a;
496
+ }
497
+ let c = 0;
498
+ for (const f of e.children)
499
+ ye(f) && (c += 1, Te(
500
+ f,
501
+ l,
502
+ c,
503
+ s,
504
+ a ?? o,
505
+ i
506
+ ));
507
+ }
508
+ function nn(e, r) {
509
+ let t;
510
+ for (const n of e)
511
+ n.line <= r && (t = n);
512
+ return t ?? e[0];
513
+ }
514
+ function bt(e) {
515
+ const r = e.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, " ");
516
+ return /<\s*slide(?:show)?[\s/>]/.test(r) ? "slides" : "html";
517
+ }
518
+ const Ee = /* @__PURE__ */ new Set([
519
+ "colophon",
520
+ "biography",
521
+ "dedication",
522
+ "acknowledgement",
523
+ "preface",
524
+ "chapter",
525
+ "section",
526
+ "subsection",
527
+ "subsubsection",
528
+ "slide",
529
+ "appendix",
530
+ "index",
531
+ "exercises",
532
+ "reading-questions",
533
+ "references",
534
+ "solutions",
535
+ "glossary",
536
+ "worksheet",
537
+ "handout"
538
+ ]), Se = /* @__PURE__ */ new Set([
539
+ "book",
540
+ "article",
541
+ "slideshow",
542
+ "letter",
543
+ "memo"
544
+ ]), ke = /* @__PURE__ */ new Set([
545
+ ...Se,
546
+ ...Ee,
547
+ "part",
548
+ "frontmatter",
549
+ "backmatter"
550
+ ]), gt = /* @__PURE__ */ new Set(["docinfo"]), $e = /* @__PURE__ */ new Set([
551
+ "title",
552
+ "shorttitle",
553
+ "plaintitle",
554
+ "subtitle",
555
+ "caption"
556
+ ]);
557
+ function b(e) {
558
+ return e.type === "element";
559
+ }
560
+ function Ie(e) {
561
+ return e.attributes["xml:id"] ?? void 0;
562
+ }
563
+ function X(e, r) {
564
+ for (const t of e.children) {
565
+ if (!b(t)) continue;
566
+ if (Ie(t) === r) return t;
567
+ const n = X(t, r);
568
+ if (n) return n;
569
+ }
570
+ }
571
+ function A(e, r) {
572
+ for (const t of e.children) {
573
+ if (!b(t)) continue;
574
+ if (t === r) return [t];
575
+ const n = A(t, r);
576
+ if (n) return [t, ...n];
577
+ }
578
+ }
579
+ function vt(e, r) {
580
+ let t = 0;
581
+ for (const [n, o] of e.entries()) {
582
+ const i = n === 0 ? void 0 : e[n - 1];
583
+ Se.has(o.name) ? t = 0 : o.name === "frontmatter" || o.name === "backmatter" || o.name === "part" ? t = i?.name === "book" && r ? 1 : 0 : Ee.has(o.name) && (t += 1);
584
+ }
585
+ return t;
586
+ }
587
+ function Pe(e) {
588
+ if (!$e.has(e.name)) {
589
+ e.children = e.children.filter(b);
590
+ for (const r of e.children)
591
+ Pe(r);
592
+ }
593
+ }
594
+ function U(e, r) {
595
+ const t = [];
596
+ for (const n of e.children)
597
+ b(n) && (r.has(n) || gt.has(n.name) || $e.has(n.name) ? t.push(n) : ke.has(n.name) && (U(n, r), t.push(n)));
598
+ e.children = t;
599
+ }
600
+ function xt(e) {
601
+ const r = /* @__PURE__ */ new Set(), t = (n) => {
602
+ if (n.name === "xref") {
603
+ const o = [
604
+ n.attributes.ref,
605
+ n.attributes.first,
606
+ n.attributes.last
607
+ ];
608
+ for (const i of o)
609
+ for (const s of i?.split(/[\s,]+/) ?? [])
610
+ s && r.add(s);
611
+ }
612
+ for (const o of n.children)
613
+ b(o) && t(o);
614
+ };
615
+ return t(e), r;
616
+ }
617
+ function yt(e, r, t) {
618
+ const n = (o) => {
619
+ for (const [i, s] of o.children.entries())
620
+ if (b(s)) {
621
+ if (Ie(s) === r)
622
+ return o.children[i] = t, !0;
623
+ if (n(s)) return !0;
624
+ }
625
+ return !1;
626
+ };
627
+ return n(e);
628
+ }
629
+ function Tt(e, r) {
630
+ const t = X(e, r);
631
+ if (!t) return;
632
+ const n = A(e, t);
633
+ if (!n) return;
634
+ const o = n.filter(
635
+ (h) => h.name !== "pretext" && h.name !== "mathbook"
636
+ ), i = o[0], s = i?.name === "book" && i.children.some(
637
+ (h) => b(h) && h.name === "part"
638
+ ), a = vt(o, s), l = new Set(n), c = xt(t), f = /* @__PURE__ */ new Set();
639
+ if (c.size > 0)
640
+ for (const h of c) {
641
+ const m = X(e, h);
642
+ if (!m || m === t) continue;
643
+ const R = A(e, m);
644
+ if (!R) continue;
645
+ const g = [...R].reverse().find((w) => ke.has(w.name));
646
+ if (g && g !== t) {
647
+ f.add(g);
648
+ for (const w of A(e, g) ?? [])
649
+ l.add(w);
650
+ }
651
+ }
652
+ for (const h of n) {
653
+ if (h === t) break;
654
+ U(h, l);
655
+ }
656
+ for (const h of f)
657
+ Pe(h);
658
+ const p = e.children.find(b);
659
+ return p && !n.includes(p) && U(p, l), { content: I(e), divisionId: r, level: a };
660
+ }
661
+ const Et = "This live preview shows a single division, so links into the rest of the document do not work here.", St = /<a\b([^>]*)>/g, kt = /\sid="([^"]*)"/g, _ = /\shref="([^"]*)"/, $t = /\\href\{([^{}]*)\}/g, oe = /\stitle="([^"]*)"/;
662
+ function It(e) {
663
+ const r = /* @__PURE__ */ new Set();
664
+ for (const t of e.matchAll(kt))
665
+ r.add(t[1]);
666
+ return r;
667
+ }
668
+ function Pt(e) {
669
+ return e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
670
+ }
671
+ function ie(e, r) {
672
+ if (e.startsWith("#") || e === "") return { kind: "keep" };
673
+ if (/^[a-z][a-z0-9+.-]*:/i.test(e) || e.startsWith("//"))
674
+ return { kind: "keep" };
675
+ const t = e.indexOf("#");
676
+ if (t === -1) return { kind: "off-page" };
677
+ const n = e.slice(t + 1);
678
+ return r.has(n) && n !== "" ? { kind: "same-page", id: n } : { kind: "off-page" };
679
+ }
680
+ function Rt(e, r = {}) {
681
+ const t = r.offPageMessage ?? Et, n = It(e);
682
+ return e.replace(St, (i, s) => {
683
+ const a = _.exec(s);
684
+ if (!a) return i;
685
+ const l = ie(a[1], n);
686
+ if (l.kind === "keep") return i;
687
+ if (l.kind === "same-page")
688
+ return `<a${s.replace(_, ` href="#${l.id}"`)}>`;
689
+ const c = oe.exec(s), f = Pt(t), p = c ? `${c[1]} — ${f}` : f;
690
+ return `<a${s.replace(_, "").replace(oe, "")} title="${p}" aria-disabled="true" style="cursor:default">`;
691
+ }).replace($t, (i, s) => {
692
+ const a = ie(s, n);
693
+ return a.kind === "same-page" ? `\\href{#${a.id}}` : i;
694
+ });
695
+ }
696
+ function Dt() {
697
+ return We(C(), "xsl");
698
+ }
699
+ function At() {
700
+ return "Suspending" in WebAssembly;
701
+ }
702
+ function Lt() {
703
+ if (!At())
704
+ throw new Error(
705
+ "WebAssembly JSPI is not enabled. Run Node with --experimental-wasm-jspi (or use the pretext-html CLI, which re-launches itself with the flag)."
706
+ );
707
+ }
708
+ function $(e) {
709
+ return e.includes("'") ? e.includes('"') ? `concat(${e.split("'").map((t) => `'${t}'`).join(`, "'", `)})` : `"${e}"` : `'${e}'`;
710
+ }
711
+ function Mt(e) {
712
+ return e.replace(
713
+ /from '\.\/(https:\/\/[^']+)'/g,
714
+ (r, t) => `from '${t}'`
715
+ );
716
+ }
717
+ const jt = `https://cdn.jsdelivr.net/gh/PreTeXtBook/html-static@${pe}/dist/`, _t = {
718
+ "cli.version": pe,
719
+ // Colon-delimited: pretext-runestone.xsl splits them with str:tokenize.
720
+ "rs-js": Je.join(":"),
721
+ "rs-css": ze.join(":"),
722
+ "rs-version": He
723
+ };
724
+ function Bt(e) {
725
+ return e.replace(
726
+ /(<link\b[^>]*\bhref=["'])_static\//gi,
727
+ `$1${jt}_static/`
728
+ );
729
+ }
730
+ function Ot(e) {
731
+ return e.slice(0, 65536).replace(/<\?[\s\S]*?\?>/g, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<!DOCTYPE[^>]*>/gi, " ").match(/<\s*([A-Za-z][\w:-]*)/)?.[1];
732
+ }
733
+ async function se(e) {
734
+ const r = await M(e);
735
+ if (r === void 0)
736
+ throw new Error(
737
+ `Could not read ${e}. (In the browser build only absolute URLs are readable — pass sourceContent instead of relying on sourcePath.)`
738
+ );
739
+ return r;
740
+ }
741
+ async function Ct(e, r) {
742
+ const t = d.resolve(e), n = await M(t);
743
+ if (n !== void 0)
744
+ return pt(n, t, r);
745
+ }
746
+ function Ft(e, r, t, n) {
747
+ const o = e.replace(/^\uFEFF/, "").replace(/<\?xml[\s\S]*?\?>/, "").replace(/<!DOCTYPE[^>]*>/i, ""), i = t === "slides" ? "slideshow" : r === "chapter" || r === "part" ? "book" : "article";
748
+ return `<pretext>
749
+ ${n?.trim() ? `${n.trim()}
750
+ ` : ""}<${i}>
751
+ <title/>
752
+ ${o}
753
+ </${i}>
754
+ </pretext>
755
+ `;
756
+ }
757
+ async function Nt(e, r, t) {
758
+ const n = E(e).children.find(
759
+ (c) => c.type === "element"
760
+ ), o = n?.attributes["xml:id"];
761
+ if (!n || !o) return;
762
+ const i = d.resolve(r), s = await M(i);
763
+ if (s === void 0) return;
764
+ let a;
765
+ try {
766
+ a = (await J(s, i, t)).tree;
767
+ } catch {
768
+ return;
769
+ }
770
+ if (!yt(a, o, n)) return;
771
+ const l = Tt(a, o);
772
+ if (l)
773
+ return { ...l, tree: a };
774
+ }
775
+ let Re = !1;
776
+ function Xt(e) {
777
+ return e instanceof Error && /\bAborted\(/.test(e.message);
778
+ }
779
+ function Ut(e) {
780
+ return e instanceof Error && /memory access|out of bounds/i.test(e.message);
781
+ }
782
+ function Wt() {
783
+ if (Re)
784
+ throw new Error(
785
+ "The WebAssembly renderer aborted earlier in this process and cannot be reused. Restart the process (or reload the page) to render again."
786
+ );
787
+ }
788
+ function ae(e) {
789
+ if (Xt(e))
790
+ throw Re = !0, new Error(
791
+ "The WebAssembly renderer aborted and cannot be reused. Restart the process (or reload the page) to render again.",
792
+ { cause: e }
793
+ );
794
+ if (Ut(e))
795
+ throw new Error(
796
+ "The WebAssembly renderer hit a memory fault. Most often the document is too large for this build's stack — whole-book builds are a known limitation, so try a smaller document, or see the README about rebuilding libxslt-wasm with a larger stack. The same fault also results from running two renders concurrently against the shared libxslt state.",
797
+ { cause: e }
798
+ );
799
+ }
800
+ let B;
801
+ async function De() {
802
+ return B || (B = (async () => {
803
+ const [e, r] = await Promise.all([
804
+ import("@pretextbook/libxslt-wasm"),
805
+ import("@pretextbook/libxslt-wasm/exslt")
806
+ ]);
807
+ return r.registerAll(), e;
808
+ })()), B;
809
+ }
810
+ const O = /* @__PURE__ */ new Map(), Ht = {
811
+ html: "preview-html.xsl",
812
+ slides: "preview-revealjs.xsl"
813
+ };
814
+ async function Jt(e, r) {
815
+ const t = Ht[r], n = `${r}\0${e}`;
816
+ let o = O.get(n);
817
+ return o || (o = (async () => {
818
+ const { XmlDocument: i, XsltStylesheet: s } = await De(), a = be(e), l = await de(C(), `/${t}`);
819
+ if (l === void 0)
820
+ throw new Error(
821
+ `Could not read ${t} from ${C()}. Check the assets location (PRETEXT_HTML_ASSETS under Node, setAssetsBase() in the browser).`
822
+ );
823
+ const c = new TextDecoder().decode(l), f = await i.fromString(c, {
824
+ url: `${a}/${t}`,
825
+ options: { noEnt: !0, dtdLoad: !0, huge: !0 }
826
+ });
827
+ return s.fromXmlDocument(f);
828
+ })(), O.set(n, o), o.catch(() => O.delete(n))), o;
829
+ }
830
+ let ce = Promise.resolve();
831
+ function zt(e) {
832
+ const r = ce.then(
833
+ () => le(e),
834
+ () => le(e)
835
+ );
836
+ return ce = r.catch(() => {
837
+ }), r;
838
+ }
839
+ async function le(e) {
840
+ Lt(), Wt();
841
+ const { XmlDocument: r } = await De(), t = d.resolve(e.sourcePath), n = d.resolve(
842
+ e.projectDir ?? d.dirname(t)
843
+ ), o = e.sourceContent ?? await se(t), i = d.relative(n, t);
844
+ if (i.startsWith(".."))
845
+ throw new Error(
846
+ `sourcePath (${t}) must live inside projectDir (${n})`
847
+ );
848
+ const s = d.relative(
849
+ n,
850
+ d.resolve(e.mainSourcePath ?? t)
851
+ ), a = s.startsWith("..") ? i : s, l = be(n);
852
+ try {
853
+ let c, f = `${l}/__pretext-preview-publication.xml`;
854
+ if (e.publicationPath) {
855
+ const u = d.resolve(e.publicationPath);
856
+ c = await se(u);
857
+ const v = d.relative(n, u);
858
+ v.startsWith("..") || (f = `${l}/${v.split(d.sep).join("/")}`);
859
+ }
860
+ let p, h;
861
+ if (e.sourceMap) {
862
+ const u = await J(
863
+ o,
864
+ t,
865
+ n
866
+ );
867
+ p = u.tree, h = u.content;
868
+ } else
869
+ h = await ut(
870
+ o,
871
+ t,
872
+ n
873
+ );
874
+ const m = e.target ?? bt(h), R = await Jt(e.xslDir ?? Dt(), m), g = ct(c, {
875
+ cssTheme: e.cssTheme,
876
+ revealTheme: e.revealTheme,
877
+ target: m
878
+ });
879
+ rt(f, g);
880
+ const w = lt(g), z = d.dirname(d.resolve(n, a)), K = w.external !== void 0 && w.generated !== void 0 ? {
881
+ external: d.resolve(z, w.external),
882
+ generated: d.resolve(z, w.generated)
883
+ } : void 0, D = Ot(h);
884
+ let V = { parentId: "root", position: 1 }, S, G = t;
885
+ if (D !== "pretext" && D !== "mathbook") {
886
+ if (!e.fragment)
887
+ throw new Error(
888
+ `The document root is <${D ?? "?"}>, not <pretext> — this looks like an xi:included fragment. Render the project's main source file instead, or pass the fragment option.`
889
+ );
890
+ if (e.contextSourcePath) {
891
+ const u = await Nt(
892
+ h,
893
+ e.contextSourcePath,
894
+ n
895
+ );
896
+ u && (h = u.content, p = e.sourceMap ? u.tree : void 0, G = d.resolve(e.contextSourcePath), S = { divisionId: u.divisionId, level: u.level });
897
+ }
898
+ if (!S) {
899
+ const u = e.docinfo ?? (e.docinfoSourcePath ? await Ct(e.docinfoSourcePath, n) : void 0);
900
+ h = Ft(
901
+ h,
902
+ D,
903
+ m,
904
+ u
905
+ ), V = { parentId: u?.trim() ? "root-1-2" : "root-1-1", position: 2 };
906
+ }
907
+ }
908
+ const Y = p ? wt(p, G, V) : void 0, q = await r.fromString(h, {
909
+ url: `${l}/${a.split(d.sep).join("/")}`,
910
+ options: { noEnt: !0, dtdLoad: !0, huge: !0 }
911
+ }), k = {
912
+ publisher: $(f)
913
+ };
914
+ for (const [u, v] of Object.entries(_t))
915
+ k[u] = $(v);
916
+ S && (k.subtree = $(S.divisionId), k["subtree-level"] = $(String(S.level)));
917
+ for (const [u, v] of Object.entries(e.stringParams ?? {}))
918
+ k[u] = $(v);
919
+ let j;
920
+ try {
921
+ j = await R.apply(q, k);
922
+ } catch (u) {
923
+ throw ae(u), new Error(
924
+ "PreTeXt XSLT transform failed; see the PTX:ERROR/PTX:FATAL messages on stderr for details.",
925
+ { cause: u }
926
+ );
927
+ } finally {
928
+ q.delete();
929
+ }
930
+ try {
931
+ let u = Mt(j.toHtmlString());
932
+ return u = Rt(u, {
933
+ ...e.offPageMessage !== void 0 ? { offPageMessage: e.offPageMessage } : {}
934
+ }), (e.openKnowls ?? !0) && (u = qe(u)), m === "slides" ? (u = Bt(u), u = Le(u, e.revealView ?? "scroll", {
935
+ zoom: e.revealZoom
936
+ })) : e.theme && (u = Me(u, e.theme)), e.previewBanner && (u = Ue(u, e.previewBanner)), {
937
+ html: u,
938
+ target: m,
939
+ ...Y ? { sourceMap: Y } : {},
940
+ ...K ? { assetDirs: K } : {}
941
+ };
942
+ } catch (u) {
943
+ throw ae(u), u;
944
+ } finally {
945
+ j.delete();
946
+ }
947
+ } finally {
948
+ nt(l);
949
+ }
950
+ }
951
+ const ue = `Usage: pretext-html <source.ptx> [options]
952
+
953
+ Render a PreTeXt document to a single standalone HTML page (portable build:
954
+ one page, css/js/MathJax from CDN). No PreTeXt installation required.
955
+
956
+ Options:
957
+ -o, --output <file> Write HTML to a file instead of stdout
958
+ --publication <file> Publication file (portable html is forced on)
959
+ --css-theme <name> HTML theme to use when the publication file names
960
+ none of its own: default-modern (PreTeXt's default),
961
+ denver, tacoma, salem, greeley or boulder
962
+ --project-dir <dir> Directory served to the transform for xi:includes
963
+ (default: the source file's directory)
964
+ --param <name=value> Extra XSLT string parameter (repeatable)
965
+ --xsl-dir <dir> Use PreTeXt stylesheets from this directory
966
+ instead of the vendored copy
967
+ --fragment Allow a non-root source file: wrap the fragment
968
+ in a minimal <pretext> document (an <article>,
969
+ or a <book> for <chapter>/<part> fragments)
970
+ --docinfo <file> File whose contents (a <docinfo> element) are
971
+ injected into the wrapper in --fragment mode, so
972
+ the fragment keeps the project's LaTeX macros
973
+ --docinfo-source <file> Complete source file (e.g. main.ptx) to lift the
974
+ <docinfo> from for --fragment mode, resolving
975
+ xi:includes (used when --docinfo is not given)
976
+ --context <file> Complete document (e.g. main.ptx) the fragment
977
+ belongs to. Renders it in place, so it is numbered
978
+ and its cross-references resolve as in the built
979
+ book. Supersedes --docinfo-source
980
+ --off-page-message <text> Tooltip on links whose target is not on the
981
+ previewed page, which cannot navigate in a
982
+ single-page preview
983
+ --source-map <file> Also write a JSON source map: HTML id → source
984
+ file/line for every element (for editor sync)
985
+ --no-open-knowls Leave solutions, hints and other born-hidden
986
+ knowls collapsed, as a real PreTeXt build does.
987
+ They are expanded by default, since these pages
988
+ are previews of work in progress
989
+ -h, --help Show this help
990
+ `;
991
+ function Kt(e) {
992
+ let r, t, n, o;
993
+ const i = {}, s = {};
994
+ for (let a = 0; a < e.length; a++) {
995
+ const l = e[a], c = () => {
996
+ if (a += 1, a >= e.length)
997
+ throw new Error(`Missing value for ${l}`);
998
+ return e[a];
999
+ };
1000
+ switch (l) {
1001
+ case "-h":
1002
+ case "--help":
1003
+ y.stdout.write(ue), y.exit(0);
1004
+ break;
1005
+ case "-o":
1006
+ case "--output":
1007
+ t = c();
1008
+ break;
1009
+ case "--publication":
1010
+ i.publicationPath = c();
1011
+ break;
1012
+ case "--css-theme":
1013
+ i.cssTheme = c();
1014
+ break;
1015
+ case "--project-dir":
1016
+ i.projectDir = c();
1017
+ break;
1018
+ case "--xsl-dir":
1019
+ i.xslDir = c();
1020
+ break;
1021
+ case "--fragment":
1022
+ i.fragment = !0;
1023
+ break;
1024
+ case "--no-open-knowls":
1025
+ i.openKnowls = !1;
1026
+ break;
1027
+ case "--docinfo":
1028
+ n = c();
1029
+ break;
1030
+ case "--docinfo-source":
1031
+ i.docinfoSourcePath = c();
1032
+ break;
1033
+ case "--context":
1034
+ i.contextSourcePath = c();
1035
+ break;
1036
+ case "--off-page-message":
1037
+ i.offPageMessage = c();
1038
+ break;
1039
+ case "--source-map":
1040
+ o = c(), i.sourceMap = !0;
1041
+ break;
1042
+ case "--param": {
1043
+ const f = c(), p = f.indexOf("=");
1044
+ if (p === -1)
1045
+ throw new Error(`--param expects name=value, got: ${f}`);
1046
+ s[f.slice(0, p)] = f.slice(p + 1);
1047
+ break;
1048
+ }
1049
+ default:
1050
+ if (l.startsWith("-"))
1051
+ throw new Error(`Unknown option: ${l}`);
1052
+ if (r)
1053
+ throw new Error(`Unexpected extra argument: ${l}`);
1054
+ r = l;
1055
+ }
1056
+ }
1057
+ if (!r)
1058
+ throw new Error(`No source file given.
1059
+
1060
+ ` + ue);
1061
+ return Object.keys(s).length > 0 && (i.stringParams = s), {
1062
+ options: { ...i, sourcePath: r },
1063
+ output: t,
1064
+ docinfoPath: n,
1065
+ sourceMapPath: o
1066
+ };
1067
+ }
1068
+ async function rn(e) {
1069
+ const { options: r, output: t, docinfoPath: n, sourceMapPath: o } = Kt(e);
1070
+ n && (r.docinfo = await W(n, "utf8"));
1071
+ const i = Date.now(), { html: s, sourceMap: a } = await zt(r);
1072
+ y.stderr.write(`pretext-html: rendered in ${Date.now() - i}ms
1073
+ `), o && a && (await Z(o, JSON.stringify(a, null, 2)), y.stderr.write(`pretext-html: wrote ${o}
1074
+ `)), t ? (await Z(t, s), y.stderr.write(`pretext-html: wrote ${t}
1075
+ `)) : y.stdout.write(s);
1076
+ }
1077
+ export {
1078
+ Et as O,
1079
+ je as P,
1080
+ bt as a,
1081
+ Tt as b,
1082
+ vt as c,
1083
+ Dt as d,
1084
+ ct as e,
1085
+ nn as f,
1086
+ At as g,
1087
+ yt as h,
1088
+ Ue as i,
1089
+ Rt as j,
1090
+ tn as k,
1091
+ Kt as l,
1092
+ rn as m,
1093
+ qe as o,
1094
+ Ce as p,
1095
+ zt as r,
1096
+ en as s,
1097
+ $ as x
1098
+ };