@pretextbook/pretext-html 0.16.1 → 0.17.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.
@@ -0,0 +1,1456 @@
1
+ import { readFile as ue, writeFile as xe } from "node:fs/promises";
2
+ import * as L from "node:process";
3
+ import { listPrintouts as st, rootPrintoutId as at, injectPrintPreview as Me } from "./printout.js";
4
+ import * as f from "node:path";
5
+ import { fileURLToPath as ct } from "node:url";
6
+ import { fromXml as U } from "xast-util-from-xml";
7
+ import { toXml as B } from "xast-util-to-xml";
8
+ import { injectRevealBridge as lt } from "./reveal.js";
9
+ import { injectThemeBridge as ut } from "./theme.js";
10
+ const dt = "ptxPreviewBannerDismissed", ft = "About live preview";
11
+ function ye(e) {
12
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13
+ }
14
+ function ht(e) {
15
+ return [
16
+ '<div id="ptx-preview-banner" class="ptx-preview-banner" role="note">',
17
+ ` <span class="ptx-preview-banner-text">${ye(e)}</span>`,
18
+ ' <button type="button" class="ptx-preview-banner-dismiss" aria-label="Dismiss">&times;</button>',
19
+ "</div>",
20
+ `<button type="button" id="ptx-preview-banner-restore" class="ptx-preview-banner-restore" hidden>${ye(ft)}</button>`
21
+ ].join(`
22
+ `);
23
+ }
24
+ function pt() {
25
+ return [
26
+ "<style>",
27
+ ".ptx-preview-banner {",
28
+ " display: flex;",
29
+ " align-items: center;",
30
+ " gap: 0.75em;",
31
+ " padding: 0.5em 1em;",
32
+ " font-size: 0.9em;",
33
+ " background: #fff8e1;",
34
+ " color: #6b5300;",
35
+ " border-bottom: 1px solid #f0c36d;",
36
+ "}",
37
+ // The [hidden] content attribute only hides an element via the
38
+ // user-agent stylesheet's `[hidden] { display: none }` rule, and a
39
+ // normal-priority *author* rule always wins over a normal-priority UA
40
+ // rule regardless of selector specificity — so the unconditional
41
+ // `display: flex` above would otherwise keep the banner visible even
42
+ // after previewBannerScript sets `banner.hidden = true`.
43
+ ".ptx-preview-banner[hidden] {",
44
+ " display: none;",
45
+ "}",
46
+ ".ptx-preview-banner-text {",
47
+ " flex: 1 1 auto;",
48
+ "}",
49
+ ".ptx-preview-banner-dismiss {",
50
+ " flex: 0 0 auto;",
51
+ " background: transparent;",
52
+ " border: none;",
53
+ " font-size: 1.2em;",
54
+ " line-height: 1;",
55
+ " cursor: pointer;",
56
+ " color: inherit;",
57
+ " padding: 0 0.25em;",
58
+ "}",
59
+ "html.dark-mode .ptx-preview-banner {",
60
+ " background: #3a2f00;",
61
+ " color: #f2d27a;",
62
+ " border-bottom-color: #6b5300;",
63
+ "}",
64
+ ".ptx-preview-banner-restore {",
65
+ " position: fixed;",
66
+ " right: 0.5em;",
67
+ " bottom: 0.5em;",
68
+ " z-index: 1000;",
69
+ " background: transparent;",
70
+ " border: none;",
71
+ " padding: 0.15em 0.4em;",
72
+ " font-size: 0.75em;",
73
+ " color: inherit;",
74
+ " opacity: 0.45;",
75
+ " text-decoration: underline;",
76
+ " cursor: pointer;",
77
+ "}",
78
+ ".ptx-preview-banner-restore:hover {",
79
+ " opacity: 0.85;",
80
+ "}",
81
+ // Printing a preview is a real workflow — the print-preview layout exists
82
+ // to be printed from (see printout.ts) — and a warning about the preview
83
+ // has no business on the paper.
84
+ "@media print {",
85
+ " .ptx-preview-banner, .ptx-preview-banner-restore {",
86
+ " display: none !important;",
87
+ " }",
88
+ "}",
89
+ "</style>"
90
+ ].join(`
91
+ `);
92
+ }
93
+ function mt() {
94
+ return [
95
+ "<script>",
96
+ "(function () {",
97
+ " var KEY = " + JSON.stringify(dt) + ";",
98
+ ' var banner = document.getElementById("ptx-preview-banner");',
99
+ ' var restore = document.getElementById("ptx-preview-banner-restore");',
100
+ " if (!banner || !restore) { return; }",
101
+ // localStorage can throw (disabled cookies/storage, sandboxed iframe);
102
+ // fall back to always showing the banner rather than crashing the page.
103
+ " function isDismissed() {",
104
+ " try { return window.localStorage.getItem(KEY) === '1'; }",
105
+ " catch (e) { return false; }",
106
+ " }",
107
+ " function setDismissed(value) {",
108
+ " try {",
109
+ " if (value) { window.localStorage.setItem(KEY, '1'); }",
110
+ " else { window.localStorage.removeItem(KEY); }",
111
+ " } catch (e) {}",
112
+ " }",
113
+ " function apply(dismissed) {",
114
+ " banner.hidden = dismissed;",
115
+ " restore.hidden = !dismissed;",
116
+ " }",
117
+ " apply(isDismissed());",
118
+ ' var dismissBtn = banner.querySelector(".ptx-preview-banner-dismiss");',
119
+ " if (dismissBtn) {",
120
+ " dismissBtn.addEventListener('click', function () {",
121
+ " setDismissed(true);",
122
+ " apply(true);",
123
+ " });",
124
+ " }",
125
+ " restore.addEventListener('click', function () {",
126
+ " setDismissed(false);",
127
+ " apply(false);",
128
+ " });",
129
+ "})();",
130
+ "<\/script>"
131
+ ].join(`
132
+ `);
133
+ }
134
+ const bt = "<!--ptx-preview-banner-->", wt = "<!--/ptx-preview-banner-->", vt = /\n?<!--ptx-preview-banner-->[\s\S]*?<!--\/ptx-preview-banner-->/;
135
+ function gt(e, n) {
136
+ const t = vt.exec(e), r = t ? e.slice(0, t.index) + e.slice(t.index + t[0].length) : e;
137
+ if (!/<body[^>]*>/i.test(r))
138
+ return r;
139
+ const i = `
140
+ ${bt}
141
+ ${pt()}
142
+ ${ht(n.message)}
143
+ ${mt()}
144
+ ${wt}`;
145
+ return r.replace(/<body([^>]*)>/i, `<body$1>${i}`);
146
+ }
147
+ async function je(e, n) {
148
+ const t = f.resolve(e), r = f.resolve(t, `.${f.posix.normalize(n)}`);
149
+ if (r !== t && !r.startsWith(t + f.sep)) {
150
+ console.error(
151
+ `pretext-html: refusing to serve ${n} from outside ${t}`
152
+ );
153
+ return;
154
+ }
155
+ try {
156
+ return new Uint8Array(await ue(r));
157
+ } catch {
158
+ return;
159
+ }
160
+ }
161
+ async function xt(e) {
162
+ try {
163
+ return await ue(e, "utf8");
164
+ } catch {
165
+ return;
166
+ }
167
+ }
168
+ let Ce;
169
+ function Xn(e) {
170
+ Ce = e;
171
+ }
172
+ function se() {
173
+ return Ce ?? process.env.PRETEXT_HTML_ASSETS ?? f.resolve(f.dirname(ct(import.meta.url)), "..", "assets");
174
+ }
175
+ function yt(e, n) {
176
+ return f.join(e, n);
177
+ }
178
+ const Oe = "2.55.0", Tt = "8.3.3", St = [
179
+ "prefix-runtime.dd07797f77620576.bundle.js",
180
+ "prefix-926.5c7038c8ef173faf.bundle.js",
181
+ "prefix-runestone.11d27cdc38930c2c.bundle.js"
182
+ ], kt = [
183
+ "prefix-926.229729d09512acc9.css",
184
+ "prefix-runestone.347e2ecdeacfed99.css"
185
+ ], Et = "born-hidden-knowl", $t = /<details\b([^>]*)>(\s*)(?:<summary\b([^>]*)>)?/g;
186
+ function Pt(e) {
187
+ const n = /\bclass="([^"]*)"/.exec(e);
188
+ return n ? n[1].trim().split(/\s+/) : [];
189
+ }
190
+ function It(e) {
191
+ return /(^|\s)open(\s|=|$)/.test(e);
192
+ }
193
+ function Te(e) {
194
+ return It(e) ? e : ` open${e}`;
195
+ }
196
+ function Lt(e) {
197
+ return e.replace(
198
+ $t,
199
+ (n, t, r, i) => {
200
+ if (!Pt(t).includes(Et))
201
+ return n;
202
+ const o = `<details${Te(t)}>`;
203
+ return i === void 0 ? `${o}${r}` : `${o}${r}<summary${Te(i)}>`;
204
+ }
205
+ );
206
+ }
207
+ const de = /* @__PURE__ */ new Map(), _e = /* @__PURE__ */ new Map();
208
+ let ae, Dt = 0, Be = je;
209
+ function Un(e) {
210
+ Be = e;
211
+ }
212
+ function Rt(e, n) {
213
+ const t = typeof e == "string" ? e : e instanceof URL ? e.href : e.url, r = _e.get(t);
214
+ if (r !== void 0)
215
+ return Promise.resolve(new Response(r, { status: 200 }));
216
+ let i;
217
+ try {
218
+ i = new URL(t).host;
219
+ } catch {
220
+ i = "";
221
+ }
222
+ const o = de.get(i);
223
+ return o !== void 0 ? Mt(o, t) : ae(e, n);
224
+ }
225
+ const At = '<?xml version="1.0"?><pretext-preview-missing-file/>';
226
+ async function Mt(e, n) {
227
+ const t = decodeURIComponent(new URL(n).pathname), r = await Be(e, t);
228
+ if (r === void 0)
229
+ return new Response(At, { status: 200 });
230
+ const i = typeof r == "string" ? r : new Uint8Array(r);
231
+ return new Response(i, { status: 200 });
232
+ }
233
+ function Ne() {
234
+ ae || (ae = globalThis.fetch, globalThis.fetch = Rt);
235
+ }
236
+ function Fe(e) {
237
+ Ne();
238
+ const n = `mnt${++Dt}.ptx.invalid`;
239
+ return de.set(n, e), `http://${n}`;
240
+ }
241
+ function jt(e) {
242
+ de.delete(new URL(e).host);
243
+ }
244
+ function Ct(e, n) {
245
+ Ne(), _e.set(e, n);
246
+ }
247
+ const J = `<?xml version="1.0" encoding="UTF-8"?>
248
+ <publication>
249
+ <source>
250
+ <directories external="../assets" generated="../generated-assets" />
251
+ </source>
252
+ <common>
253
+ <tableofcontents level="0"/>
254
+ </common>
255
+ <html>
256
+ <platform portable="yes"/>
257
+ </html>
258
+ </publication>
259
+ `, Ot = ["theme", "style", "shell"];
260
+ function $(e, n) {
261
+ for (const t of e.children)
262
+ if (t.type === "element" && t.name === n)
263
+ return t;
264
+ }
265
+ function _(e, n) {
266
+ let t = $(e, n);
267
+ return t || (t = { type: "element", name: n, attributes: {}, children: [] }, e.children.push(t)), t;
268
+ }
269
+ function _t(e, n) {
270
+ const t = $(e, "css");
271
+ if (t && Ot.some((i) => t.attributes?.[i]))
272
+ return;
273
+ const r = t ?? _(e, "css");
274
+ r.attributes = { ...r.attributes, theme: n };
275
+ }
276
+ function Bt(e, n) {
277
+ const t = $(e, "appearance");
278
+ if (t?.attributes?.theme)
279
+ return;
280
+ const r = t ?? _(e, "appearance");
281
+ r.attributes = { ...r.attributes, theme: n };
282
+ }
283
+ function Nt(e) {
284
+ const n = _(e, "resources");
285
+ n.attributes = {
286
+ ...n.attributes,
287
+ host: "cdn",
288
+ math: "online"
289
+ };
290
+ }
291
+ function Ft(e, n = {}) {
292
+ const t = n.cssTheme?.trim(), r = n.revealTheme?.trim(), i = n.target === "slides";
293
+ if (!e && !t && !i)
294
+ return J;
295
+ const o = U(e ?? J), s = $(o, "publication");
296
+ if (!s)
297
+ throw new Error(
298
+ "Invalid publication file: no root <publication> element found"
299
+ );
300
+ const a = _(s, "html"), c = _(a, "platform");
301
+ if (c.attributes = { ...c.attributes, portable: "yes" }, t && _t(a, t), i) {
302
+ const u = _(s, "revealjs");
303
+ Nt(u), r && Bt(u, r);
304
+ }
305
+ return B(o);
306
+ }
307
+ function Se(e) {
308
+ if (!(e === void 0 || e === "" || e.startsWith("/")))
309
+ return e.endsWith("/") ? e : `${e}/`;
310
+ }
311
+ function ke(e) {
312
+ const n = e?.attributes?.level;
313
+ if (n == null || n === "") return;
314
+ const t = Number(n);
315
+ return Number.isFinite(t) && t >= 0 ? t : void 0;
316
+ }
317
+ function Xt(e) {
318
+ const n = U(e ?? J), t = $(n, "publication"), r = t && $(t, "common"), i = r && $(r, "tableofcontents"), o = t && $(t, "numbering");
319
+ let s, a;
320
+ for (const c of o?.children ?? []) {
321
+ if (c.type !== "element") continue;
322
+ const u = ke(c);
323
+ u !== void 0 && (c.name === "divisions" ? a = u : s = Math.max(s ?? 0, u));
324
+ }
325
+ return {
326
+ tocLevel: ke(i),
327
+ numberingLevel: s,
328
+ maxNumberingLevel: a
329
+ };
330
+ }
331
+ function Ut(e) {
332
+ const n = U(e ?? J), t = $(n, "publication"), r = t && $(t, "source"), i = r && $(r, "directories"), o = Se(
333
+ i?.attributes?.external ?? void 0
334
+ ), s = Se(
335
+ i?.attributes?.generated ?? void 0
336
+ );
337
+ return {
338
+ external: o,
339
+ generated: s,
340
+ managed: o !== void 0 && s !== void 0
341
+ };
342
+ }
343
+ const Xe = "http://www.w3.org/2001/XInclude", Ee = 64;
344
+ async function Z(e, n) {
345
+ return (e?.readSource ? await e.readSource(n) : void 0) ?? await xt(n);
346
+ }
347
+ function fe(e, n, t) {
348
+ return e.cache ? e.cache.parse(n, t) : U(t);
349
+ }
350
+ async function he(e, n, t, r = {}) {
351
+ const i = f.resolve(n), o = fe(r, i, e), s = {
352
+ ...r,
353
+ projectDir: f.resolve(t),
354
+ stack: [i],
355
+ files: [i],
356
+ bytes: e.length
357
+ }, a = await X(
358
+ o,
359
+ f.dirname(i),
360
+ { "": "" },
361
+ s
362
+ );
363
+ return {
364
+ tree: o,
365
+ content: a ? B(o) : e,
366
+ files: s.files,
367
+ bytes: s.bytes
368
+ };
369
+ }
370
+ function Ue(e) {
371
+ return e.data?.ptxIncludedFile;
372
+ }
373
+ function We(e, n) {
374
+ const t = e.data ?? (e.data = {});
375
+ t.ptxIncludedFile = n;
376
+ }
377
+ function K(e, n) {
378
+ let t = e;
379
+ for (const [r, i] of Object.entries(n.attributes))
380
+ (r === "xmlns" || r.startsWith("xmlns:")) && (t === e && (t = { ...e }), t[r === "xmlns" ? "" : r.slice(6)] = i ?? "");
381
+ return t;
382
+ }
383
+ function He(e, n) {
384
+ const t = e.name.indexOf(":"), r = t === -1 ? "" : e.name.slice(0, t);
385
+ return (t === -1 ? e.name : e.name.slice(t + 1)) === "include" && n[r] === Xe;
386
+ }
387
+ async function X(e, n, t, r) {
388
+ let i = !1;
389
+ for (let o = 0; o < e.children.length; o++) {
390
+ const s = e.children[o];
391
+ if (s.type !== "element")
392
+ continue;
393
+ const a = K(t, s);
394
+ if (He(s, a)) {
395
+ const c = await ze(
396
+ s,
397
+ n,
398
+ a,
399
+ r
400
+ );
401
+ e.children.splice(o, 1, ...c), o += c.length - 1, i = !0;
402
+ } else await X(s, n, a, r) && (i = !0);
403
+ }
404
+ return i;
405
+ }
406
+ async function ze(e, n, t, r) {
407
+ const i = r.stack[r.stack.length - 1];
408
+ if (e.attributes.xpointer)
409
+ throw new Error(
410
+ `xi:include with xpointer is not supported by the JS preview build (in ${i})`
411
+ );
412
+ const o = e.attributes.href;
413
+ if (!o)
414
+ throw new Error(`xi:include without href (in ${i})`);
415
+ let s = o;
416
+ try {
417
+ s = decodeURIComponent(o);
418
+ } catch {
419
+ }
420
+ const a = f.resolve(n, s);
421
+ if (a !== r.projectDir && !a.startsWith(r.projectDir + f.sep))
422
+ throw new Error(
423
+ `xi:include escapes the project directory: ${o} (in ${i})`
424
+ );
425
+ if (r.stack.includes(a))
426
+ throw new Error(
427
+ `Circular xi:include: ${[...r.stack, a].join(" -> ")}`
428
+ );
429
+ if (r.stack.length >= Ee)
430
+ throw new Error(`xi:include nesting exceeds ${Ee} levels`);
431
+ const c = await Z(r, a);
432
+ if (c === void 0) {
433
+ const l = Ht(e, t);
434
+ if (l)
435
+ return await X(l, n, t, r), l.children;
436
+ throw new Error(`xi:include target not found: ${o} (in ${i})`);
437
+ }
438
+ if (r.files.includes(a) || r.files.push(a), r.bytes += c.length, e.attributes.parse === "text")
439
+ return [{ type: "text", value: c }];
440
+ let u;
441
+ try {
442
+ u = fe(r, a, c);
443
+ } catch (l) {
444
+ throw new Error(
445
+ `Could not parse xi:include target ${a} as XML: ${l instanceof Error ? l.message : l}. (Included files must be well-formed, with a single root element.)`
446
+ );
447
+ }
448
+ r.stack.push(a);
449
+ try {
450
+ await X(u, f.dirname(a), { "": "" }, r);
451
+ } finally {
452
+ r.stack.pop();
453
+ }
454
+ const d = u.children.filter(
455
+ (l) => l.type === "element"
456
+ );
457
+ for (const l of d)
458
+ We(l, a);
459
+ return d;
460
+ }
461
+ function $e(e) {
462
+ return e.name === "docinfo";
463
+ }
464
+ async function Wt(e, n, t, r = {}) {
465
+ try {
466
+ const i = f.resolve(n), s = fe(r, i, e).children.find(
467
+ (d) => d.type === "element"
468
+ );
469
+ if (!s)
470
+ return;
471
+ const a = f.dirname(i), c = {
472
+ ...r,
473
+ projectDir: f.resolve(t),
474
+ stack: [i],
475
+ files: [i],
476
+ bytes: e.length
477
+ }, u = K({ "": "" }, s);
478
+ for (const d of s.children) {
479
+ if (d.type !== "element")
480
+ continue;
481
+ const l = K(u, d);
482
+ if ($e(d))
483
+ return await X(d, a, l, c), B(d);
484
+ if (He(d, l)) {
485
+ const w = (await ze(
486
+ d,
487
+ a,
488
+ l,
489
+ c
490
+ )).find(
491
+ (T) => T.type === "element" && $e(T)
492
+ );
493
+ if (w)
494
+ return B(w);
495
+ continue;
496
+ }
497
+ break;
498
+ }
499
+ return;
500
+ } catch {
501
+ return;
502
+ }
503
+ }
504
+ function Ht(e, n) {
505
+ for (const t of e.children) {
506
+ if (t.type !== "element")
507
+ continue;
508
+ const r = K(n, t), i = t.name.indexOf(":"), o = i === -1 ? "" : t.name.slice(0, i);
509
+ if ((i === -1 ? t.name : t.name.slice(i + 1)) === "fallback" && r[o] === Xe)
510
+ return t;
511
+ }
512
+ }
513
+ function Je(e) {
514
+ return typeof e == "object" && e !== null && e.type === "element";
515
+ }
516
+ function zt(e) {
517
+ return /^(pf|xhtml):/.test(e.name);
518
+ }
519
+ function Jt(e, n, t) {
520
+ const r = [];
521
+ return Ke(e, n, t, (i, o, s, a) => {
522
+ r.push({
523
+ id: o,
524
+ file: s,
525
+ line: i.position?.start.line ?? 1,
526
+ column: i.position?.start.column ?? 1,
527
+ endLine: i.position?.end.line ?? i.position?.start.line ?? 1,
528
+ parent: a
529
+ });
530
+ }), r;
531
+ }
532
+ function Kt(e, n, t) {
533
+ let r;
534
+ return Ke(e, "", t, (i, o) => {
535
+ if (i === n)
536
+ return r = o, !1;
537
+ }), r;
538
+ }
539
+ function Ke(e, n, t, r) {
540
+ const i = e.children.find(Je);
541
+ if (i) {
542
+ const o = t?.parentId ?? "root";
543
+ Ve(i, o, t?.position ?? 1, n, o, r);
544
+ }
545
+ }
546
+ function Ve(e, n, t, r, i, o) {
547
+ const s = Ue(e) ?? r;
548
+ let a, c;
549
+ if (zt(e))
550
+ c = "root";
551
+ else {
552
+ const d = e.attributes.label ?? e.attributes["xml:id"];
553
+ if (a = d ?? `${n}-${t}`, o(e, a, s, i) === !1)
554
+ return !1;
555
+ c = a;
556
+ }
557
+ let u = 0;
558
+ for (const d of e.children)
559
+ if (Je(d) && (u += 1, !Ve(
560
+ d,
561
+ c,
562
+ u,
563
+ s,
564
+ a ?? i,
565
+ o
566
+ )))
567
+ return !1;
568
+ return !0;
569
+ }
570
+ function Wn(e, n) {
571
+ let t;
572
+ for (const r of e)
573
+ r.line <= n && (t = r);
574
+ return t ?? e[0];
575
+ }
576
+ function Vt(e) {
577
+ const n = e.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, " ");
578
+ return /<\s*slide(?:show)?[\s/>]/.test(n) ? "slides" : "html";
579
+ }
580
+ const Ge = /* @__PURE__ */ new Set([
581
+ "colophon",
582
+ "biography",
583
+ "dedication",
584
+ "acknowledgement",
585
+ "preface",
586
+ "chapter",
587
+ "section",
588
+ "subsection",
589
+ "subsubsection",
590
+ "slide",
591
+ "appendix",
592
+ "index",
593
+ "exercises",
594
+ "reading-questions",
595
+ "references",
596
+ "solutions",
597
+ "glossary",
598
+ "worksheet",
599
+ "handout"
600
+ ]), Ye = /* @__PURE__ */ new Set([
601
+ "book",
602
+ "article",
603
+ "slideshow",
604
+ "letter",
605
+ "memo"
606
+ ]), A = /* @__PURE__ */ new Set([
607
+ ...Ye,
608
+ ...Ge,
609
+ "part",
610
+ "foreword",
611
+ "frontmatter",
612
+ "backmatter"
613
+ ]);
614
+ function Q(e) {
615
+ return A.has(e.name);
616
+ }
617
+ const Gt = /* @__PURE__ */ new Set(["docinfo"]), qe = /* @__PURE__ */ new Set([
618
+ "title",
619
+ "shorttitle",
620
+ "plaintitle",
621
+ "subtitle",
622
+ "caption"
623
+ ]);
624
+ function x(e) {
625
+ return e.type === "element";
626
+ }
627
+ function Ze(e) {
628
+ return e.attributes["xml:id"] ?? void 0;
629
+ }
630
+ function V(e, n) {
631
+ for (const t of e.children) {
632
+ if (!x(t)) continue;
633
+ if (Ze(t) === n) return t;
634
+ const r = V(t, n);
635
+ if (r) return r;
636
+ }
637
+ }
638
+ function R(e, n) {
639
+ for (const t of e.children) {
640
+ if (!x(t)) continue;
641
+ if (t === n) return [t];
642
+ const r = R(t, n);
643
+ if (r) return [t, ...r];
644
+ }
645
+ }
646
+ function Qe(e, n, t, r) {
647
+ return Ye.has(t) ? 0 : t === "frontmatter" || t === "backmatter" || t === "part" ? n === "book" && r ? 1 : 0 : Ge.has(t) ? e + 1 : e;
648
+ }
649
+ function G(e, n) {
650
+ let t = 0;
651
+ for (const [r, i] of e.entries()) {
652
+ const o = r === 0 ? void 0 : e[r - 1];
653
+ t = Qe(t, o?.name, i.name, n);
654
+ }
655
+ return t;
656
+ }
657
+ function pe(e) {
658
+ return e?.name === "book" && e.children.some(
659
+ (n) => x(n) && n.name === "part"
660
+ );
661
+ }
662
+ function O(e, n) {
663
+ return e.children.some(
664
+ (t) => x(t) && n.includes(t.name)
665
+ );
666
+ }
667
+ function Yt(e) {
668
+ if (!e) return 0;
669
+ const n = e.name;
670
+ if (n === "book") {
671
+ const t = pe(e), r = t ? e.children.some(
672
+ (o) => x(o) && o.name === "part" && O(o, ["chapter"])
673
+ ) : O(e, ["chapter"]), i = (t ? e.children.flatMap(
674
+ (o) => x(o) && o.name === "part" ? o.children : []
675
+ ) : e.children).some(
676
+ (o) => x(o) && o.name === "chapter" && O(o, ["section"])
677
+ );
678
+ return t && i ? 3 : t && r || i ? 2 : r ? 1 : 0;
679
+ }
680
+ if (n === "article") {
681
+ const t = e.children.filter(
682
+ (r) => x(r) && r.name === "section"
683
+ );
684
+ if (t.some((r) => O(r, ["subsection"])))
685
+ return 2;
686
+ if (t.length > 0 || O(e, ["worksheet", "handout"]))
687
+ return 1;
688
+ }
689
+ return 0;
690
+ }
691
+ function qt(e) {
692
+ return e ? e.name === "book" ? pe(e) ? 3 : 2 : e.name === "article" && O(e, ["section", "worksheet", "handout"]) ? 1 : 0 : 0;
693
+ }
694
+ function me(e) {
695
+ if (!qe.has(e.name)) {
696
+ e.children = e.children.filter(x);
697
+ for (const n of e.children)
698
+ me(n);
699
+ }
700
+ }
701
+ function Zt(e) {
702
+ for (const n of e.children)
703
+ x(n) && !A.has(n.name) && me(n);
704
+ }
705
+ function Qt(e, n = !1) {
706
+ const t = /* @__PURE__ */ new Set(), r = (i) => {
707
+ if (!(n && i !== e && A.has(i.name))) {
708
+ if (i.name === "xref") {
709
+ const o = [
710
+ i.attributes.ref,
711
+ i.attributes.first,
712
+ i.attributes.last
713
+ ];
714
+ for (const s of o)
715
+ for (const a of s?.split(/[\s,]+/) ?? [])
716
+ a && t.add(a);
717
+ }
718
+ for (const o of i.children)
719
+ x(o) && r(o);
720
+ }
721
+ };
722
+ return r(e), t;
723
+ }
724
+ function en(e, n, t) {
725
+ const r = (i) => {
726
+ for (const [o, s] of i.children.entries())
727
+ if (x(s)) {
728
+ if (Ze(s) === n)
729
+ return i.children[o] = t, !0;
730
+ if (r(s)) return !0;
731
+ }
732
+ return !1;
733
+ };
734
+ return r(e);
735
+ }
736
+ function et(e, n, t) {
737
+ const r = t.contentKept.has(e), i = t.chain.has(e), o = [];
738
+ for (const s of e.children) {
739
+ if (!x(s)) {
740
+ r && o.push(s);
741
+ continue;
742
+ }
743
+ if (t.verbatim.has(s) || Gt.has(s.name) || qe.has(s.name))
744
+ o.push(s);
745
+ else if (A.has(s.name)) {
746
+ const a = Qe(
747
+ n,
748
+ e.name,
749
+ s.name,
750
+ t.hasParts
751
+ );
752
+ (t.chain.has(s) || i || a <= t.tocLevel) && (et(s, a, t), o.push(s));
753
+ } else r && o.push(s);
754
+ }
755
+ e.children = o;
756
+ }
757
+ function tn(e, n, t = {}) {
758
+ const r = V(e, n);
759
+ if (!r) return;
760
+ const i = R(e, r);
761
+ if (!i) return;
762
+ const o = (t.collapseChildren ?? !1) && r.children.some(
763
+ (p) => x(p) && A.has(p.name)
764
+ ), s = (p) => p.name === "pretext" || p.name === "mathbook", a = i.filter((p) => !s(p)), c = a[0], u = pe(c), d = G(a, u), l = (p) => {
765
+ const S = R(e, p)?.filter((b) => !s(b)) ?? [];
766
+ return G(S, u);
767
+ }, m = t.maxNumberingLevel ?? 1 / 0, w = Math.min(
768
+ t.numberingLevel ?? qt(c),
769
+ m
770
+ ), T = t.tocLevel ?? Yt(c), y = {
771
+ verbatim: /* @__PURE__ */ new Set(),
772
+ chain: new Set(i),
773
+ contentKept: /* @__PURE__ */ new Set(),
774
+ tocLevel: T,
775
+ hasParts: u
776
+ }, M = /* @__PURE__ */ new Set(), P = /* @__PURE__ */ new Set();
777
+ o ? y.contentKept.add(r) : y.verbatim.add(r);
778
+ const ee = (p) => {
779
+ const S = R(e, p);
780
+ if (!S) return;
781
+ const b = S.filter((v) => A.has(v.name)), k = b[b.length - 1];
782
+ if (!k || k === r) return;
783
+ if (i.includes(k)) {
784
+ y.contentKept.add(k), P.add(k);
785
+ return;
786
+ }
787
+ let I = k;
788
+ for (let v = b.length - 1; v >= 0 && !(l(b[v]) <= w || i.includes(b[v - 1])); v--)
789
+ I = b[v - 1] ?? I;
790
+ if (!y.verbatim.has(I)) {
791
+ y.verbatim.add(I), M.add(I);
792
+ for (const v of R(e, I) ?? []) y.chain.add(v);
793
+ }
794
+ };
795
+ for (const p of Qt(r, o)) {
796
+ const S = V(e, p);
797
+ S && S !== r && ee(S);
798
+ }
799
+ for (const p of [...y.chain]) {
800
+ if (!A.has(p.name) || l(p) <= w)
801
+ continue;
802
+ const S = R(e, p)?.at(-2);
803
+ if (S)
804
+ for (const b of S.children) {
805
+ if (b === p) break;
806
+ !x(b) || !A.has(b.name) || y.verbatim.has(b) || y.chain.has(b) || (y.verbatim.add(b), M.add(b));
807
+ }
808
+ }
809
+ const j = e.children.find(x);
810
+ j && et(j, 0, y);
811
+ for (const p of M)
812
+ me(p);
813
+ for (const p of P)
814
+ Zt(p);
815
+ return { content: B(e), divisionId: n, level: d, collapsed: o };
816
+ }
817
+ class nn {
818
+ entries = /* @__PURE__ */ new Map();
819
+ maxEntries;
820
+ /** Cumulative hit/miss counts, for diagnostics and tests. */
821
+ hits = 0;
822
+ misses = 0;
823
+ constructor(n = {}) {
824
+ this.maxEntries = Math.max(1, n.maxEntries ?? 1024);
825
+ }
826
+ /**
827
+ * The parse of `content`, as a fresh tree the caller may mutate freely.
828
+ * `key` names the file — a path — so that a changed file replaces its own
829
+ * entry instead of accumulating one per version.
830
+ *
831
+ * Throws what `fromXml` throws for malformed content; nothing is cached
832
+ * then.
833
+ */
834
+ parse(n, t) {
835
+ const r = this.entries.get(n);
836
+ if (r && r.content === t)
837
+ return this.hits += 1, this.entries.delete(n), this.entries.set(n, r), ce(r.tree);
838
+ this.misses += 1;
839
+ const i = U(t);
840
+ for (this.entries.delete(n), this.entries.set(n, { content: t, tree: i }); this.entries.size > this.maxEntries; ) {
841
+ const o = this.entries.keys().next().value;
842
+ if (o === void 0) break;
843
+ this.entries.delete(o);
844
+ }
845
+ return ce(i);
846
+ }
847
+ /** Number of files currently held. */
848
+ get size() {
849
+ return this.entries.size;
850
+ }
851
+ /** Drop one file (when the host knows it is gone), or everything. */
852
+ invalidate(n) {
853
+ n === void 0 ? this.entries.clear() : this.entries.delete(n);
854
+ }
855
+ }
856
+ function ce(e) {
857
+ const n = { ...e };
858
+ return n.attributes && (n.attributes = { ...n.attributes }), n.children && (n.children = n.children.map((t) => ce(t))), n.data && (n.data = { ...n.data }), n;
859
+ }
860
+ function Y(e) {
861
+ return typeof e == "object" && e !== null && e.type === "element";
862
+ }
863
+ function rn(e) {
864
+ for (let n = e.length - 1; n >= 0; n--)
865
+ if (Q(e[n])) return e[n];
866
+ }
867
+ function on(e, n, t) {
868
+ const r = [], i = (s, a, c) => {
869
+ const u = Ue(s) ?? a, d = [...c, s];
870
+ u === t && Q(s) && r.push({ element: s, chain: d });
871
+ for (const l of s.children)
872
+ Y(l) && i(l, u, d);
873
+ }, o = e.children.find(Y);
874
+ return o && i(o, n, []), r;
875
+ }
876
+ function sn(e, n, t, r) {
877
+ const i = on(e, n, t);
878
+ let o, s, a;
879
+ for (const { element: c } of i) {
880
+ const u = c.position?.start.line, d = c.position?.end.line ?? u;
881
+ u === void 0 || d === void 0 || (u <= r && r <= d ? o = c : u > r ? s ??= c : a = c);
882
+ }
883
+ return o ?? s ?? a;
884
+ }
885
+ function an(e, n, t) {
886
+ let r;
887
+ if (n.id !== void 0) {
888
+ const l = V(e, n.id), m = l && R(e, l);
889
+ r = m && rn(m);
890
+ } else if (n.line !== void 0) {
891
+ const l = f.resolve(t), m = f.resolve(n.file ?? t);
892
+ r = sn(e, l, m, n.line);
893
+ }
894
+ if (!r) return;
895
+ const i = n.level ?? 1 / 0;
896
+ let o = !1;
897
+ const a = (R(e, r) ?? [r]).filter(
898
+ (l) => l.name !== "pretext" && l.name !== "mathbook"
899
+ ), c = a[0], u = c?.name === "book" && c.children.some(
900
+ (l) => Y(l) && l.name === "part"
901
+ ), d = G(a, u);
902
+ if (d > i)
903
+ for (let l = 0; l < a.length; l++) {
904
+ const m = a[l];
905
+ if (Q(m) && G(a.slice(0, l + 1), u) >= i) {
906
+ r = m;
907
+ break;
908
+ }
909
+ }
910
+ else d < i && (o = !0);
911
+ return { element: r, id: tt(e, r), collapse: o };
912
+ }
913
+ function tt(e, n) {
914
+ const t = n.attributes["xml:id"];
915
+ if (t) return t;
916
+ const r = Kt(e, n) ?? "root-1";
917
+ return n.attributes["xml:id"] = r, r;
918
+ }
919
+ function q(e) {
920
+ let n = 0;
921
+ for (const t of e.children)
922
+ Y(t) && (n += 1 + q(t));
923
+ return n;
924
+ }
925
+ const cn = "This live preview shows a single division, so links into the rest of the document do not work here.", ln = /<a\b([^>]*)>/g, un = /\sid="([^"]*)"/g, ne = /\shref="([^"]*)"/, dn = /\\href\{([^{}]*)\}/g, Pe = /\stitle="([^"]*)"/;
926
+ function fn(e) {
927
+ const n = /* @__PURE__ */ new Set();
928
+ for (const t of e.matchAll(un))
929
+ n.add(t[1]);
930
+ return n;
931
+ }
932
+ function re(e) {
933
+ return e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
934
+ }
935
+ function Ie(e, n) {
936
+ if (e.startsWith("#") || e === "") return { kind: "keep" };
937
+ if (/^[a-z][a-z0-9+.-]*:/i.test(e) || e.startsWith("//"))
938
+ return { kind: "keep" };
939
+ const t = e.indexOf("#"), i = (t === -1 ? e : e.slice(0, t)).replace(/\.html?$/i, "");
940
+ if (t === -1) return { kind: "off-page", division: i };
941
+ const o = e.slice(t + 1);
942
+ return n.has(o) && o !== "" ? { kind: "same-page", id: o } : { kind: "off-page", division: i, ...o ? { id: o } : {} };
943
+ }
944
+ function hn(e, n = {}) {
945
+ const t = n.offPageMessage ?? cn, r = fn(e);
946
+ return e.replace(ln, (o, s) => {
947
+ const a = ne.exec(s);
948
+ if (!a) return o;
949
+ const c = Ie(a[1], r);
950
+ if (c.kind === "keep") return o;
951
+ if (c.kind === "same-page")
952
+ return `<a${s.replace(ne, ` href="#${c.id}"`)}>`;
953
+ const u = Pe.exec(s), d = re(t), l = u ? `${u[1]} — ${d}` : d, w = s.replace(ne, "").replace(Pe, ""), T = ` data-ptx-division="${re(c.division)}"` + (c.id ? ` data-ptx-id="${re(c.id)}"` : "");
954
+ return `<a${w} title="${l}" aria-disabled="true" style="cursor:default"${T}>`;
955
+ }).replace(dn, (o, s) => {
956
+ const a = Ie(s, r);
957
+ return a.kind === "same-page" ? `\\href{#${a.id}}` : o;
958
+ });
959
+ }
960
+ function pn() {
961
+ return yt(se(), "xsl");
962
+ }
963
+ function mn() {
964
+ return "Suspending" in WebAssembly;
965
+ }
966
+ function bn() {
967
+ if (!mn())
968
+ throw new Error(
969
+ "WebAssembly JSPI is not enabled. Run Node with --experimental-wasm-jspi (or use the pretext-html CLI, which re-launches itself with the flag)."
970
+ );
971
+ }
972
+ function F(e) {
973
+ return e.includes("'") ? e.includes('"') ? `concat(${e.split("'").map((t) => `'${t}'`).join(`, "'", `)})` : `"${e}"` : `'${e}'`;
974
+ }
975
+ function wn(e) {
976
+ return e.replace(
977
+ /from '\.\/(https:\/\/[^']+)'/g,
978
+ (n, t) => `from '${t}'`
979
+ );
980
+ }
981
+ const vn = `https://cdn.jsdelivr.net/gh/PreTeXtBook/html-static@${Oe}/dist/`, gn = {
982
+ "cli.version": Oe,
983
+ // Colon-delimited: pretext-runestone.xsl splits them with str:tokenize.
984
+ "rs-js": St.join(":"),
985
+ "rs-css": kt.join(":"),
986
+ "rs-version": Tt
987
+ };
988
+ function xn(e) {
989
+ return e.replace(
990
+ /(<link\b[^>]*\bhref=["'])_static\//gi,
991
+ `$1${vn}_static/`
992
+ );
993
+ }
994
+ function yn(e) {
995
+ return e.slice(0, 65536).replace(/<\?[\s\S]*?\?>/g, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<!DOCTYPE[^>]*>/gi, " ").match(/<\s*([A-Za-z][\w:-]*)/)?.[1];
996
+ }
997
+ let Tn;
998
+ function Sn() {
999
+ return Tn ??= new nn();
1000
+ }
1001
+ function nt(e) {
1002
+ const n = e.files ? new Map(
1003
+ Object.entries(e.files).map(([o, s]) => [
1004
+ f.resolve(o),
1005
+ s
1006
+ ])
1007
+ ) : void 0, t = e.readSource, r = n || t ? async (o) => n?.get(o) ?? await t?.(o) : void 0, i = e.cache === !1 ? void 0 : e.cache ?? Sn();
1008
+ return { readSource: r, cache: i };
1009
+ }
1010
+ async function le(e, n) {
1011
+ const t = await Z(e, n);
1012
+ if (t === void 0)
1013
+ throw new Error(
1014
+ `Could not read ${n}. (In the browser build only absolute URLs are readable — pass sourceContent instead of relying on sourcePath.)`
1015
+ );
1016
+ return t;
1017
+ }
1018
+ async function kn(e, n, t) {
1019
+ const r = f.resolve(e), i = await Z(t, r);
1020
+ if (i !== void 0)
1021
+ return Wt(i, r, n, t);
1022
+ }
1023
+ async function Hn(e) {
1024
+ const n = nt(e), t = f.resolve(e.sourcePath), r = f.resolve(
1025
+ e.projectDir ?? f.dirname(t)
1026
+ ), i = e.sourceContent ?? await le(n, t), o = await he(
1027
+ i,
1028
+ t,
1029
+ r,
1030
+ n
1031
+ );
1032
+ return {
1033
+ bytes: o.bytes,
1034
+ elements: q(o.tree),
1035
+ files: o.files
1036
+ };
1037
+ }
1038
+ function En(e, n, t, r) {
1039
+ const i = e.replace(/^\uFEFF/, "").replace(/<\?xml[\s\S]*?\?>/, "").replace(/<!DOCTYPE[^>]*>/i, ""), o = t === "slides" ? "slideshow" : n === "chapter" || n === "part" ? "book" : "article";
1040
+ return `<pretext>
1041
+ ${r?.trim() ? `${r.trim()}
1042
+ ` : ""}<${o}>
1043
+ <title/>
1044
+ ${i}
1045
+ </${o}>
1046
+ </pretext>
1047
+ `;
1048
+ }
1049
+ async function $n(e, n, t, r, i, o) {
1050
+ const s = e.attributes["xml:id"];
1051
+ if (!s) return;
1052
+ const a = f.resolve(t), c = i ?? await Z(o, a);
1053
+ if (c === void 0) return;
1054
+ let u, d, l;
1055
+ try {
1056
+ ({ tree: u, files: d, bytes: l } = await he(
1057
+ c,
1058
+ a,
1059
+ r,
1060
+ o
1061
+ ));
1062
+ } catch {
1063
+ return;
1064
+ }
1065
+ if (We(e, n), !!en(u, s, e))
1066
+ return d.includes(n) || d.push(n), { tree: u, divisionId: s, files: d, bytes: l };
1067
+ }
1068
+ let rt = !1;
1069
+ function Pn(e) {
1070
+ return e instanceof Error && /\bAborted\(/.test(e.message);
1071
+ }
1072
+ function In(e) {
1073
+ return e instanceof Error && /memory access|out of bounds/i.test(e.message);
1074
+ }
1075
+ function Ln() {
1076
+ if (rt)
1077
+ throw new Error(
1078
+ "The WebAssembly renderer aborted earlier in this process and cannot be reused. Restart the process (or reload the page) to render again."
1079
+ );
1080
+ }
1081
+ function Le(e) {
1082
+ if (Pn(e))
1083
+ throw rt = !0, new Error(
1084
+ "The WebAssembly renderer aborted and cannot be reused. Restart the process (or reload the page) to render again.",
1085
+ { cause: e }
1086
+ );
1087
+ if (In(e))
1088
+ throw new Error(
1089
+ "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.",
1090
+ { cause: e }
1091
+ );
1092
+ }
1093
+ let ie;
1094
+ async function it() {
1095
+ return ie || (ie = (async () => {
1096
+ const [e, n] = await Promise.all([
1097
+ import("@pretextbook/libxslt-wasm"),
1098
+ import("@pretextbook/libxslt-wasm/exslt")
1099
+ ]);
1100
+ return n.registerAll(), e;
1101
+ })()), ie;
1102
+ }
1103
+ const oe = /* @__PURE__ */ new Map(), Dn = {
1104
+ html: "preview-html.xsl",
1105
+ slides: "preview-revealjs.xsl"
1106
+ };
1107
+ async function Rn(e, n) {
1108
+ const t = Dn[n], r = `${n}\0${e}`;
1109
+ let i = oe.get(r);
1110
+ return i || (i = (async () => {
1111
+ const { XmlDocument: o, XsltStylesheet: s } = await it(), a = Fe(e), c = await je(se(), `/${t}`);
1112
+ if (c === void 0)
1113
+ throw new Error(
1114
+ `Could not read ${t} from ${se()}. Check the assets location (PRETEXT_HTML_ASSETS under Node, setAssetsBase() in the browser).`
1115
+ );
1116
+ const u = new TextDecoder().decode(c), d = await o.fromString(u, {
1117
+ url: `${a}/${t}`,
1118
+ options: { noEnt: !0, dtdLoad: !0, huge: !0 }
1119
+ });
1120
+ return s.fromXmlDocument(d);
1121
+ })(), oe.set(r, i), i.catch(() => oe.delete(r))), i;
1122
+ }
1123
+ let De = Promise.resolve();
1124
+ function An(e) {
1125
+ const n = De.then(
1126
+ () => Re(e),
1127
+ () => Re(e)
1128
+ );
1129
+ return De = n.catch(() => {
1130
+ }), n;
1131
+ }
1132
+ async function Re(e) {
1133
+ bn(), Ln();
1134
+ const { XmlDocument: n } = await it(), t = nt(e), r = f.resolve(e.sourcePath), i = f.resolve(
1135
+ e.projectDir ?? f.dirname(r)
1136
+ ), o = e.sourceContent ?? await le(t, r), s = f.relative(i, r);
1137
+ if (s.startsWith(".."))
1138
+ throw new Error(
1139
+ `sourcePath (${r}) must live inside projectDir (${i})`
1140
+ );
1141
+ const a = f.relative(
1142
+ i,
1143
+ f.resolve(e.mainSourcePath ?? r)
1144
+ ), c = a.startsWith("..") ? s : a, u = Fe(i);
1145
+ try {
1146
+ let d, l = `${u}/__pretext-preview-publication.xml`;
1147
+ if (e.publicationPath) {
1148
+ const h = f.resolve(e.publicationPath);
1149
+ d = await le(t, h);
1150
+ const g = f.relative(i, h);
1151
+ g.startsWith("..") || (l = `${u}/${g.split(f.sep).join("/")}`);
1152
+ }
1153
+ const m = await he(
1154
+ o,
1155
+ r,
1156
+ i,
1157
+ t
1158
+ );
1159
+ let w = m.tree, T = m.content, y = m.files, M = m.bytes;
1160
+ const P = e.target ?? Vt(T), ee = await Rn(e.xslDir ?? pn(), P), j = Ft(d, {
1161
+ cssTheme: e.cssTheme,
1162
+ revealTheme: e.revealTheme,
1163
+ target: P
1164
+ });
1165
+ Ct(l, j);
1166
+ const p = Ut(j), S = f.dirname(f.resolve(i, c)), b = p.external !== void 0 && p.generated !== void 0 ? {
1167
+ external: f.resolve(S, p.external),
1168
+ generated: f.resolve(S, p.generated)
1169
+ } : void 0, k = yn(T);
1170
+ let I = { parentId: "root", position: 1 }, v, be = r, W;
1171
+ const we = (h, g) => {
1172
+ W = { bytes: M, elements: q(h), files: y };
1173
+ const E = e.subtree ? an(h, e.subtree, r) : void 0, D = E?.id ?? g;
1174
+ if (!D) return !1;
1175
+ const H = e.subtree?.wholeDocumentUpTo;
1176
+ if (H !== void 0 && W.elements <= H) return !1;
1177
+ const ot = E?.collapse && E.element ? E.element.children.filter(
1178
+ (z) => z.type === "element" && Q(z)
1179
+ ).map((z) => tt(h, z)) : [], C = tn(h, D, {
1180
+ collapseChildren: E?.collapse,
1181
+ ...Xt(j)
1182
+ });
1183
+ return C ? (T = C.content, v = {
1184
+ id: C.divisionId,
1185
+ level: C.level,
1186
+ collapsed: C.collapsed,
1187
+ children: C.collapsed ? ot : []
1188
+ }, !0) : !1;
1189
+ };
1190
+ if (k !== "pretext" && k !== "mathbook") {
1191
+ if (!e.fragment)
1192
+ throw new Error(
1193
+ `The document root is <${k ?? "?"}>, not <pretext> — this looks like an xi:included fragment. Render the project's main source file instead, or pass the fragment option.`
1194
+ );
1195
+ let h = !1;
1196
+ if (e.contextSourcePath || e.contextSourceContent) {
1197
+ const g = e.contextSourcePath ?? f.join(f.dirname(r), "__pretext-preview-context.ptx"), E = w.children.find(
1198
+ (H) => H.type === "element"
1199
+ ), D = E ? await $n(
1200
+ E,
1201
+ r,
1202
+ g,
1203
+ i,
1204
+ e.contextSourceContent,
1205
+ t
1206
+ ) : void 0;
1207
+ D && (h = !0, w = D.tree, y = D.files, M = D.bytes, be = f.resolve(g), we(w, D.divisionId) || (T = B(w)));
1208
+ }
1209
+ if (!h) {
1210
+ const g = e.docinfo ?? (e.docinfoSourcePath ? await kn(
1211
+ e.docinfoSourcePath,
1212
+ i,
1213
+ t
1214
+ ) : void 0);
1215
+ T = En(
1216
+ T,
1217
+ k,
1218
+ P,
1219
+ g
1220
+ ), I = { parentId: g?.trim() ? "root-1-2" : "root-1-1", position: 2 };
1221
+ }
1222
+ } else e.subtree && we(w);
1223
+ W ??= { bytes: M, elements: q(w), files: y };
1224
+ const ve = e.sourceMap ? Jt(w, be, I) : void 0, ge = await n.fromString(T, {
1225
+ url: `${u}/${c.split(f.sep).join("/")}`,
1226
+ options: { noEnt: !0, dtdLoad: !0, huge: !0 }
1227
+ }), N = {
1228
+ publisher: F(l)
1229
+ };
1230
+ for (const [h, g] of Object.entries(gn))
1231
+ N[h] = F(g);
1232
+ v && (N.subtree = F(v.id), N["subtree-level"] = F(
1233
+ String(v.level + (v.collapsed ? 1 : 0))
1234
+ ));
1235
+ for (const [h, g] of Object.entries(e.stringParams ?? {}))
1236
+ N[h] = F(g);
1237
+ let te;
1238
+ try {
1239
+ te = await ee.apply(ge, N);
1240
+ } catch (h) {
1241
+ throw Le(h), new Error(
1242
+ "PreTeXt XSLT transform failed; see the PTX:ERROR/PTX:FATAL messages on stderr for details.",
1243
+ { cause: h }
1244
+ );
1245
+ } finally {
1246
+ ge.delete();
1247
+ }
1248
+ try {
1249
+ let h = wn(te.toHtmlString());
1250
+ h = hn(h, {
1251
+ ...e.offPageMessage !== void 0 ? { offPageMessage: e.offPageMessage } : {}
1252
+ }), (e.openKnowls ?? !0) && (h = Lt(h)), P === "slides" ? (h = xn(h), h = lt(h, e.revealView ?? "scroll", {
1253
+ zoom: e.revealZoom
1254
+ })) : e.theme && (h = ut(h, e.theme)), e.previewBanner && (h = gt(h, e.previewBanner));
1255
+ const g = P === "slides" ? [] : st(h), E = at(g, k);
1256
+ return e.printPreview && (h = Me(h, e.printPreview)), {
1257
+ html: h,
1258
+ target: P,
1259
+ printouts: g,
1260
+ size: W,
1261
+ ...v ? { subtree: v } : {},
1262
+ ...ve ? { sourceMap: ve } : {},
1263
+ ...b ? { assetDirs: b } : {},
1264
+ ...E ? { rootPrintout: E } : {}
1265
+ };
1266
+ } catch (h) {
1267
+ throw Le(h), h;
1268
+ } finally {
1269
+ te.delete();
1270
+ }
1271
+ } finally {
1272
+ jt(u);
1273
+ }
1274
+ }
1275
+ const Ae = `Usage: pretext-html <source.ptx> [options]
1276
+
1277
+ Render a PreTeXt document to a single standalone HTML page (portable build:
1278
+ one page, css/js/MathJax from CDN). No PreTeXt installation required.
1279
+
1280
+ Options:
1281
+ -o, --output <file> Write HTML to a file instead of stdout
1282
+ --publication <file> Publication file (portable html is forced on)
1283
+ --css-theme <name> HTML theme to use when the publication file names
1284
+ none of its own: default-modern (PreTeXt's default),
1285
+ denver, tacoma, salem, greeley or boulder
1286
+ --project-dir <dir> Directory served to the transform for xi:includes
1287
+ (default: the source file's directory)
1288
+ --param <name=value> Extra XSLT string parameter (repeatable)
1289
+ --xsl-dir <dir> Use PreTeXt stylesheets from this directory
1290
+ instead of the vendored copy
1291
+ --fragment Allow a non-root source file: wrap the fragment
1292
+ in a minimal <pretext> document (an <article>,
1293
+ or a <book> for <chapter>/<part> fragments)
1294
+ --docinfo <file> File whose contents (a <docinfo> element) are
1295
+ injected into the wrapper in --fragment mode, so
1296
+ the fragment keeps the project's LaTeX macros
1297
+ --docinfo-source <file> Complete source file (e.g. main.ptx) to lift the
1298
+ <docinfo> from for --fragment mode, resolving
1299
+ xi:includes (used when --docinfo is not given)
1300
+ --context <file> Complete document (e.g. main.ptx) the fragment
1301
+ belongs to. Renders it in place, so it is numbered
1302
+ and its cross-references resolve as in the built
1303
+ book. Supersedes --docinfo-source
1304
+ --off-page-message <text> Tooltip on links whose target is not on the
1305
+ previewed page, which cannot navigate in a
1306
+ single-page preview
1307
+ --source-map <file> Also write a JSON source map: HTML id → source
1308
+ file/line for every element (for editor sync)
1309
+ --no-open-knowls Leave solutions, hints and other born-hidden
1310
+ knowls collapsed, as a real PreTeXt build does.
1311
+ They are expanded by default, since these pages
1312
+ are previews of work in progress
1313
+ --print-preview[=id] Open the page in the print-preview layout: the
1314
+ named printout paginated to a paper size, with
1315
+ workspaces at their true height. Without an id, the
1316
+ document's own printout is used (a lone worksheet
1317
+ or handout), or failing that its first. The page's
1318
+ printouts are listed on stderr
1319
+ -h, --help Show this help
1320
+ `;
1321
+ function Mn(e) {
1322
+ let n, t, r, i, o;
1323
+ const s = {}, a = {};
1324
+ for (let c = 0; c < e.length; c++) {
1325
+ const u = e[c], d = () => {
1326
+ if (c += 1, c >= e.length)
1327
+ throw new Error(`Missing value for ${u}`);
1328
+ return e[c];
1329
+ };
1330
+ switch (u) {
1331
+ case "-h":
1332
+ case "--help":
1333
+ L.stdout.write(Ae), L.exit(0);
1334
+ break;
1335
+ case "-o":
1336
+ case "--output":
1337
+ t = d();
1338
+ break;
1339
+ case "--publication":
1340
+ s.publicationPath = d();
1341
+ break;
1342
+ case "--css-theme":
1343
+ s.cssTheme = d();
1344
+ break;
1345
+ case "--project-dir":
1346
+ s.projectDir = d();
1347
+ break;
1348
+ case "--xsl-dir":
1349
+ s.xslDir = d();
1350
+ break;
1351
+ case "--fragment":
1352
+ s.fragment = !0;
1353
+ break;
1354
+ case "--no-open-knowls":
1355
+ s.openKnowls = !1;
1356
+ break;
1357
+ case "--docinfo":
1358
+ r = d();
1359
+ break;
1360
+ case "--docinfo-source":
1361
+ s.docinfoSourcePath = d();
1362
+ break;
1363
+ case "--context":
1364
+ s.contextSourcePath = d();
1365
+ break;
1366
+ case "--off-page-message":
1367
+ s.offPageMessage = d();
1368
+ break;
1369
+ case "--source-map":
1370
+ i = d(), s.sourceMap = !0;
1371
+ break;
1372
+ // Attached form only (--print-preview=<id>), so that a bare
1373
+ // --print-preview cannot swallow the source file as its id.
1374
+ case "--print-preview":
1375
+ o = !0;
1376
+ break;
1377
+ case "--param": {
1378
+ const l = d(), m = l.indexOf("=");
1379
+ if (m === -1)
1380
+ throw new Error(`--param expects name=value, got: ${l}`);
1381
+ a[l.slice(0, m)] = l.slice(m + 1);
1382
+ break;
1383
+ }
1384
+ default:
1385
+ if (u.startsWith("--print-preview=")) {
1386
+ o = u.slice(16);
1387
+ break;
1388
+ }
1389
+ if (u.startsWith("-"))
1390
+ throw new Error(`Unknown option: ${u}`);
1391
+ if (n)
1392
+ throw new Error(`Unexpected extra argument: ${u}`);
1393
+ n = u;
1394
+ }
1395
+ }
1396
+ if (!n)
1397
+ throw new Error(`No source file given.
1398
+
1399
+ ` + Ae);
1400
+ return Object.keys(a).length > 0 && (s.stringParams = a), {
1401
+ options: { ...s, sourcePath: n },
1402
+ output: t,
1403
+ docinfoPath: r,
1404
+ sourceMapPath: i,
1405
+ printPreview: o
1406
+ };
1407
+ }
1408
+ async function zn(e) {
1409
+ const { options: n, output: t, docinfoPath: r, sourceMapPath: i, printPreview: o } = Mn(e);
1410
+ r && (n.docinfo = await ue(r, "utf8"));
1411
+ const s = Date.now(), a = await An(n), { sourceMap: c, printouts: u, rootPrintout: d } = a;
1412
+ let l = a.html;
1413
+ if (L.stderr.write(`pretext-html: rendered in ${Date.now() - s}ms
1414
+ `), o !== void 0) {
1415
+ for (const w of u)
1416
+ L.stderr.write(
1417
+ `pretext-html: printout ${w.id} - ${w.label}
1418
+ `
1419
+ );
1420
+ const m = typeof o == "string" ? o : d ?? u[0]?.id;
1421
+ m ? (L.stderr.write(`pretext-html: print preview of ${m}
1422
+ `), l = Me(l, m)) : L.stderr.write(
1423
+ `pretext-html: --print-preview: this document has no printouts (worksheet, handout, or project with a workspace); writing the ordinary page.
1424
+ `
1425
+ );
1426
+ }
1427
+ i && c && (await xe(i, JSON.stringify(c, null, 2)), L.stderr.write(`pretext-html: wrote ${i}
1428
+ `)), t ? (await xe(t, l), L.stderr.write(`pretext-html: wrote ${t}
1429
+ `)) : L.stdout.write(l);
1430
+ }
1431
+ export {
1432
+ cn as O,
1433
+ dt as P,
1434
+ nn as S,
1435
+ pn as a,
1436
+ tn as b,
1437
+ Vt as c,
1438
+ Sn as d,
1439
+ G as e,
1440
+ Wn as f,
1441
+ Ft as g,
1442
+ mn as h,
1443
+ gt as i,
1444
+ An as j,
1445
+ en as k,
1446
+ hn as l,
1447
+ Hn as m,
1448
+ zn as n,
1449
+ Lt as o,
1450
+ mt as p,
1451
+ Un as q,
1452
+ Xt as r,
1453
+ Xn as s,
1454
+ Mn as t,
1455
+ F as x
1456
+ };