@pretextbook/pretext-html 0.1.0 → 0.1.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 (111) hide show
  1. package/README.md +42 -3
  2. package/assets/preview-html.xsl +230 -230
  3. package/assets/upstream.json +7 -7
  4. package/assets/xsl/README.md +0 -0
  5. package/assets/xsl/entities.ent +154 -154
  6. package/assets/xsl/extract-asymptote.xsl +68 -68
  7. package/assets/xsl/extract-biblio-csl.xsl +327 -327
  8. package/assets/xsl/extract-datafile.xsl +71 -71
  9. package/assets/xsl/extract-dynamic.xsl +139 -139
  10. package/assets/xsl/extract-identity.xsl +136 -136
  11. package/assets/xsl/extract-interactive.xsl +81 -81
  12. package/assets/xsl/extract-latex-image.xsl +118 -118
  13. package/assets/xsl/extract-mermaid.xsl +58 -58
  14. package/assets/xsl/extract-mom.xsl +63 -63
  15. package/assets/xsl/extract-pg.xsl +2897 -2897
  16. package/assets/xsl/extract-prefigure.xsl +157 -157
  17. package/assets/xsl/extract-qrcode.xsl +100 -100
  18. package/assets/xsl/extract-runestone-page-template.xsl +139 -139
  19. package/assets/xsl/extract-sageplot.xsl +166 -166
  20. package/assets/xsl/extract-stack.xsl +56 -56
  21. package/assets/xsl/extract-trace.xsl +134 -134
  22. package/assets/xsl/extract-youtube.xsl +63 -63
  23. package/assets/xsl/html-symbols.xsl +3344 -3344
  24. package/assets/xsl/latex/pretext-latex-AIM.xsl +56 -56
  25. package/assets/xsl/latex/pretext-latex-CLP.xsl +148 -148
  26. package/assets/xsl/latex/pretext-latex-chaos.xsl +317 -317
  27. package/assets/xsl/latex/pretext-latex-dyslexic-font.xsl +76 -76
  28. package/assets/xsl/latex/pretext-latex-guide.xsl +104 -104
  29. package/assets/xsl/latex/pretext-latex-texstyle.xsl +862 -862
  30. package/assets/xsl/localizations/README.md +0 -0
  31. package/assets/xsl/localizations/af-ZA.xml +276 -276
  32. package/assets/xsl/localizations/bg-BG.xml +276 -276
  33. package/assets/xsl/localizations/ca-ES.xml +285 -285
  34. package/assets/xsl/localizations/cs-CZ.xml +280 -280
  35. package/assets/xsl/localizations/de-DE.xml +281 -281
  36. package/assets/xsl/localizations/en-US.xml +427 -427
  37. package/assets/xsl/localizations/es-ES.xml +287 -287
  38. package/assets/xsl/localizations/fi-FI.xml +275 -275
  39. package/assets/xsl/localizations/fr-CA.xml +276 -276
  40. package/assets/xsl/localizations/fr-FR.xml +280 -280
  41. package/assets/xsl/localizations/hu-HU.xml +280 -280
  42. package/assets/xsl/localizations/it-IT.xml +276 -276
  43. package/assets/xsl/localizations/ku-CKB.xml +275 -275
  44. package/assets/xsl/localizations/localizations.xml +48 -48
  45. package/assets/xsl/localizations/nl-NL.xml +275 -275
  46. package/assets/xsl/localizations/pt-BR.xml +288 -288
  47. package/assets/xsl/localizations/pt-PT.xml +292 -292
  48. package/assets/xsl/localizations/zh-HANS.xml +269 -269
  49. package/assets/xsl/pretext-assembly.xsl +4795 -4795
  50. package/assets/xsl/pretext-basic-html.xsl +86 -86
  51. package/assets/xsl/pretext-beamer.xsl +662 -662
  52. package/assets/xsl/pretext-braille-preprint.xsl +2249 -2249
  53. package/assets/xsl/pretext-common.xsl +12018 -12018
  54. package/assets/xsl/pretext-dynamic.xsl +65 -65
  55. package/assets/xsl/pretext-epub.xsl +1483 -1483
  56. package/assets/xsl/pretext-fo.xsl +4593 -4593
  57. package/assets/xsl/pretext-html.xsl +14791 -14791
  58. package/assets/xsl/pretext-json-manifest.xsl +150 -150
  59. package/assets/xsl/pretext-jupyter.xsl +868 -868
  60. package/assets/xsl/pretext-latex-classic.xsl +952 -952
  61. package/assets/xsl/pretext-latex-common.xsl +11563 -11563
  62. package/assets/xsl/pretext-latex.xsl +47 -47
  63. package/assets/xsl/pretext-litprog.xsl +105 -105
  64. package/assets/xsl/pretext-merge.xsl +41 -41
  65. package/assets/xsl/pretext-numbers.xsl +875 -875
  66. package/assets/xsl/pretext-revealjs.xsl +522 -522
  67. package/assets/xsl/pretext-runestone-fitb.xsl +1233 -1233
  68. package/assets/xsl/pretext-runestone-static.xsl +1438 -1438
  69. package/assets/xsl/pretext-runestone.xsl +3083 -3083
  70. package/assets/xsl/pretext-sage-doctest.xsl +289 -289
  71. package/assets/xsl/pretext-smc.xsl +492 -492
  72. package/assets/xsl/pretext-solution-manual-latex.xsl +323 -323
  73. package/assets/xsl/pretext-text-utilities.xsl +1217 -1217
  74. package/assets/xsl/pretext-text.xsl +392 -392
  75. package/assets/xsl/pretext-units.xsl +195 -195
  76. package/assets/xsl/pretext-view-source.xsl +157 -157
  77. package/assets/xsl/pretext-ww-problem-sets.xsl +391 -391
  78. package/assets/xsl/publisher-variables.xsl +4756 -4756
  79. package/assets/xsl/support/README.md +0 -0
  80. package/assets/xsl/support/extract-math.xsl +256 -256
  81. package/assets/xsl/support/package-math.xsl +141 -141
  82. package/assets/xsl/support/play-button/README.md +0 -0
  83. package/assets/xsl/support/play-button/play-button.png +0 -0
  84. package/assets/xsl/support/play-button/play-button.svg +3 -3
  85. package/assets/xsl/support/pretext-pg-macros.xsl +110 -110
  86. package/assets/xsl/support/webpack_static_imports.xml +14 -14
  87. package/assets/xsl/tests/README.md +0 -0
  88. package/assets/xsl/tests/null.xml +3 -3
  89. package/assets/xsl/tests/pretext-text-utilities-test.xsl +333 -333
  90. package/assets/xsl/utilities/README.md +0 -0
  91. package/assets/xsl/utilities/author-report.xsl +107 -107
  92. package/assets/xsl/utilities/deprecate-autoname.sed +18 -18
  93. package/assets/xsl/utilities/deprecate-index.sed +33 -33
  94. package/assets/xsl/utilities/fix-deprecations.xsl +663 -663
  95. package/assets/xsl/utilities/pretext-enhanced-source.xsl +112 -112
  96. package/assets/xsl/utilities/report-publisher-variables.xsl +162 -162
  97. package/assets/xsl/xml-to-json.xsl +326 -326
  98. package/assets/xsl/xml-to-string.xsl +328 -328
  99. package/dist/{cli-C6tfAVvi.js → cli-BQizBK0N.js} +74 -72
  100. package/dist/cli.d.ts +0 -0
  101. package/dist/cli.js +1 -1
  102. package/dist/index.d.ts +2 -0
  103. package/dist/index.js +12 -6
  104. package/dist/mounts.d.ts +0 -0
  105. package/dist/publication.d.ts +0 -0
  106. package/dist/renderer.d.ts +10 -0
  107. package/dist/sourcemap.d.ts +0 -0
  108. package/dist/theme.d.ts +68 -0
  109. package/dist/theme.js +93 -0
  110. package/dist/xinclude.d.ts +0 -0
  111. package/package.json +14 -2
@@ -1,12 +1,13 @@
1
- import { readFile as m, writeFile as j } from "node:fs/promises";
1
+ import { readFile as m, writeFile as X } from "node:fs/promises";
2
2
  import * as w from "node:process";
3
3
  import * as c from "node:path";
4
4
  import { fileURLToPath as K } from "node:url";
5
5
  import { fromXml as $ } from "xast-util-from-xml";
6
6
  import { toXml as y } from "xast-util-to-xml";
7
- const I = /* @__PURE__ */ new Map(), W = /* @__PURE__ */ new Map();
8
- let S, Q = 0;
9
- function ee(e, r) {
7
+ import { injectThemeBridge as Q } from "./theme.js";
8
+ const T = /* @__PURE__ */ new Map(), W = /* @__PURE__ */ new Map();
9
+ let S, ee = 0;
10
+ function te(e, r) {
10
11
  const t = typeof e == "string" ? e : e instanceof URL ? e.href : e.url, n = W.get(t);
11
12
  if (n !== void 0)
12
13
  return Promise.resolve(new Response(n, { status: 200 }));
@@ -16,11 +17,11 @@ function ee(e, r) {
16
17
  } catch {
17
18
  o = "";
18
19
  }
19
- const i = I.get(o);
20
- return i !== void 0 ? te(i, t) : S(e, r);
20
+ const i = T.get(o);
21
+ return i !== void 0 ? re(i, t) : S(e, r);
21
22
  }
22
23
  const F = '<?xml version="1.0"?><pretext-preview-missing-file/>';
23
- async function te(e, r) {
24
+ async function re(e, r) {
24
25
  const t = decodeURIComponent(new URL(r).pathname), n = c.resolve(e, "." + c.posix.normalize(t));
25
26
  if (n !== e && !n.startsWith(e + c.sep))
26
27
  return console.error(
@@ -33,21 +34,21 @@ async function te(e, r) {
33
34
  return new Response(F, { status: 200 });
34
35
  }
35
36
  }
36
- function N() {
37
- S || (S = globalThis.fetch, globalThis.fetch = ee);
37
+ function B() {
38
+ S || (S = globalThis.fetch, globalThis.fetch = te);
38
39
  }
39
- function B(e) {
40
- N();
41
- const r = `mnt${++Q}.ptx.invalid`;
42
- return I.set(r, c.resolve(e)), `http://${r}`;
40
+ function N(e) {
41
+ B();
42
+ const r = `mnt${++ee}.ptx.invalid`;
43
+ return T.set(r, c.resolve(e)), `http://${r}`;
43
44
  }
44
- function re(e) {
45
- I.delete(new URL(e).host);
45
+ function ne(e) {
46
+ T.delete(new URL(e).host);
46
47
  }
47
- function ne(e, r) {
48
- N(), W.set(e, r);
48
+ function oe(e, r) {
49
+ B(), W.set(e, r);
49
50
  }
50
- const oe = `<?xml version="1.0" encoding="UTF-8"?>
51
+ const ie = `<?xml version="1.0" encoding="UTF-8"?>
51
52
  <publication>
52
53
  <source>
53
54
  <directories external="../assets" generated="../generated-assets" />
@@ -68,7 +69,7 @@ function M(e, r) {
68
69
  }
69
70
  function L(e) {
70
71
  if (!e)
71
- return oe;
72
+ return ie;
72
73
  const r = $(e), t = _(r, "publication");
73
74
  if (!t)
74
75
  throw new Error(
@@ -78,7 +79,7 @@ function L(e) {
78
79
  return o.attributes = { ...o.attributes, portable: "yes" }, y(r);
79
80
  }
80
81
  const H = "http://www.w3.org/2001/XInclude", R = 64;
81
- async function ie(e, r, t) {
82
+ async function se(e, r, t) {
82
83
  return e.includes("include") ? (await J(e, r, t)).content : e;
83
84
  }
84
85
  async function J(e, r, t) {
@@ -93,10 +94,10 @@ async function J(e, r, t) {
93
94
  );
94
95
  return { tree: n, content: i ? y(n) : e };
95
96
  }
96
- function se(e) {
97
+ function ae(e) {
97
98
  return e.data?.ptxIncludedFile;
98
99
  }
99
- function ae(e, r) {
100
+ function ce(e, r) {
100
101
  const t = e.data ?? (e.data = {});
101
102
  t.ptxIncludedFile = r;
102
103
  }
@@ -158,7 +159,7 @@ async function z(e, r, t, n) {
158
159
  try {
159
160
  f = await m(s, "utf8");
160
161
  } catch {
161
- const u = le(e, t);
162
+ const u = ue(e, t);
162
163
  if (u)
163
164
  return await x(u, r, t, n), u.children;
164
165
  throw new Error(`xi:include target not found: ${i} (in ${o})`);
@@ -183,13 +184,13 @@ async function z(e, r, t, n) {
183
184
  (u) => u.type === "element"
184
185
  );
185
186
  for (const u of d)
186
- ae(u, s);
187
+ ce(u, s);
187
188
  return d;
188
189
  }
189
190
  function A(e) {
190
191
  return e.name === "docinfo";
191
192
  }
192
- async function ce(e, r, t) {
193
+ async function le(e, r, t) {
193
194
  try {
194
195
  const o = $(e).children.find(
195
196
  (a) => a.type === "element"
@@ -226,7 +227,7 @@ async function ce(e, r, t) {
226
227
  return;
227
228
  }
228
229
  }
229
- function le(e, r) {
230
+ function ue(e, r) {
230
231
  for (const t of e.children) {
231
232
  if (t.type !== "element")
232
233
  continue;
@@ -238,10 +239,10 @@ function le(e, r) {
238
239
  function G(e) {
239
240
  return typeof e == "object" && e !== null && e.type === "element";
240
241
  }
241
- function ue(e) {
242
+ function fe(e) {
242
243
  return /^(pf|xhtml):/.test(e.name);
243
244
  }
244
- function fe(e, r, t) {
245
+ function de(e, r, t) {
245
246
  const n = [], o = e.children.find(G);
246
247
  if (o) {
247
248
  const i = t?.parentId ?? "root";
@@ -250,9 +251,9 @@ function fe(e, r, t) {
250
251
  return n;
251
252
  }
252
253
  function Y(e, r, t, n, o, i) {
253
- const l = se(e) ?? n;
254
+ const l = ae(e) ?? n;
254
255
  let s, f;
255
- if (ue(e))
256
+ if (fe(e))
256
257
  f = "root";
257
258
  else {
258
259
  const d = e.attributes.label ?? e.attributes["xml:id"];
@@ -276,7 +277,7 @@ function Y(e, r, t, n, o, i) {
276
277
  i
277
278
  ));
278
279
  }
279
- function Ie(e, r) {
280
+ function De(e, r) {
280
281
  let t;
281
282
  for (const n of e)
282
283
  n.line <= r && (t = n);
@@ -285,17 +286,17 @@ function Ie(e, r) {
285
286
  function V() {
286
287
  return process.env.PRETEXT_HTML_ASSETS ?? c.resolve(c.dirname(K(import.meta.url)), "..", "assets");
287
288
  }
288
- function de() {
289
+ function pe() {
289
290
  return c.join(V(), "xsl");
290
291
  }
291
- function pe() {
292
+ function he() {
292
293
  return c.join(V(), "preview-html.xsl");
293
294
  }
294
- function he() {
295
+ function me() {
295
296
  return "Suspending" in WebAssembly;
296
297
  }
297
- function me() {
298
- if (!he())
298
+ function we() {
299
+ if (!me())
299
300
  throw new Error(
300
301
  "WebAssembly JSPI is not enabled. Run Node with --experimental-wasm-jspi (or use the pretext-html CLI, which re-launches itself with the flag)."
301
302
  );
@@ -303,16 +304,16 @@ function me() {
303
304
  function U(e) {
304
305
  return e.includes("'") ? e.includes('"') ? `concat(${e.split("'").map((t) => `'${t}'`).join(`, "'", `)})` : `"${e}"` : `'${e}'`;
305
306
  }
306
- function we(e) {
307
+ function ge(e) {
307
308
  return e.replace(
308
309
  /from '\.\/(https:\/\/[^']+)'/g,
309
310
  (r, t) => `from '${t}'`
310
311
  );
311
312
  }
312
- function ge(e) {
313
+ function be(e) {
313
314
  return e.slice(0, 65536).replace(/<\?[\s\S]*?\?>/g, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<!DOCTYPE[^>]*>/gi, " ").match(/<\s*([A-Za-z][\w:-]*)/)?.[1];
314
315
  }
315
- async function be(e, r) {
316
+ async function xe(e, r) {
316
317
  const t = c.resolve(e);
317
318
  let n;
318
319
  try {
@@ -320,9 +321,9 @@ async function be(e, r) {
320
321
  } catch {
321
322
  return;
322
323
  }
323
- return ce(n, t, r);
324
+ return le(n, t, r);
324
325
  }
325
- function xe(e, r, t) {
326
+ function ye(e, r, t) {
326
327
  const n = e.replace(/^\uFEFF/, "").replace(/<\?xml[\s\S]*?\?>/, "").replace(/<!DOCTYPE[^>]*>/i, ""), o = r === "chapter" || r === "part" ? "book" : "article";
327
328
  return `<pretext>
328
329
  ${t?.trim() ? `${t.trim()}
@@ -351,27 +352,27 @@ async function Z() {
351
352
  })()), P;
352
353
  }
353
354
  const k = /* @__PURE__ */ new Map();
354
- async function ye(e) {
355
+ async function ve(e) {
355
356
  const r = c.resolve(e);
356
357
  let t = k.get(r);
357
358
  return t || (t = (async () => {
358
- const { XmlDocument: n, XsltStylesheet: o } = await Z(), i = B(r), l = await m(pe(), "utf8"), s = await n.fromString(l, {
359
+ const { XmlDocument: n, XsltStylesheet: o } = await Z(), i = N(r), l = await m(he(), "utf8"), s = await n.fromString(l, {
359
360
  url: `${i}/preview-html.xsl`,
360
361
  options: { noEnt: !0, dtdLoad: !0, huge: !0 }
361
362
  });
362
363
  return o.fromXmlDocument(s);
363
364
  })(), k.set(r, t), t.catch(() => k.delete(r))), t;
364
365
  }
365
- async function ve(e) {
366
- me();
367
- const { XmlDocument: r } = await Z(), t = await ye(e.xslDir ?? de()), n = c.resolve(e.sourcePath), o = c.resolve(
366
+ async function $e(e) {
367
+ we();
368
+ const { XmlDocument: r } = await Z(), t = await ve(e.xslDir ?? pe()), n = c.resolve(e.sourcePath), o = c.resolve(
368
369
  e.projectDir ?? c.dirname(n)
369
370
  ), i = e.sourceContent ?? await m(n, "utf8"), l = c.relative(o, n);
370
371
  if (l.startsWith(".."))
371
372
  throw new Error(
372
373
  `sourcePath (${n}) must live inside projectDir (${o})`
373
374
  );
374
- const s = B(o);
375
+ const s = N(o);
375
376
  try {
376
377
  let f, a = `${s}/__pretext-preview-publication.xml`;
377
378
  if (e.publicationPath) {
@@ -383,7 +384,7 @@ async function ve(e) {
383
384
  b.startsWith("..") || (a = `${s}/${b.split(c.sep).join("/")}`);
384
385
  } else
385
386
  f = L();
386
- ne(a, f);
387
+ oe(a, f);
387
388
  let d, u;
388
389
  if (e.sourceMap) {
389
390
  const p = await J(
@@ -393,32 +394,32 @@ async function ve(e) {
393
394
  );
394
395
  d = p.tree, u = p.content;
395
396
  } else
396
- u = await ie(
397
+ u = await se(
397
398
  i,
398
399
  n,
399
400
  o
400
401
  );
401
- const h = ge(u);
402
+ const h = be(u);
402
403
  let g = { parentId: "root", position: 1 };
403
404
  if (h !== "pretext" && h !== "mathbook")
404
405
  if (e.fragment) {
405
- const p = e.docinfo ?? (e.docinfoSourcePath ? await be(e.docinfoSourcePath, o) : void 0);
406
- u = xe(u, h, p), g = { parentId: p?.trim() ? "root-1-2" : "root-1-1", position: 2 };
406
+ const p = e.docinfo ?? (e.docinfoSourcePath ? await xe(e.docinfoSourcePath, o) : void 0);
407
+ u = ye(u, h, p), g = { parentId: p?.trim() ? "root-1-2" : "root-1-1", position: 2 };
407
408
  } else
408
409
  throw new Error(
409
410
  `The document root is <${h ?? "?"}>, not <pretext> — this looks like an xi:included fragment. Render the project's main source file instead, or pass the fragment option.`
410
411
  );
411
- const T = d ? fe(d, n, g) : void 0, D = await r.fromString(u, {
412
+ const I = d ? de(d, n, g) : void 0, D = await r.fromString(u, {
412
413
  url: `${s}/${l.split(c.sep).join("/")}`,
413
414
  options: { noEnt: !0, dtdLoad: !0, huge: !0 }
414
- }), X = {
415
+ }), j = {
415
416
  publisher: U(a)
416
417
  };
417
418
  for (const [p, b] of Object.entries(e.stringParams ?? {}))
418
- X[p] = U(b);
419
+ j[p] = U(b);
419
420
  let E;
420
421
  try {
421
- E = await t.apply(D, X);
422
+ E = await t.apply(D, j);
422
423
  } catch (p) {
423
424
  throw C(p), new Error(
424
425
  "PreTeXt XSLT transform failed; see the PTX:ERROR/PTX:FATAL messages on stderr for details.",
@@ -428,9 +429,10 @@ async function ve(e) {
428
429
  D.delete();
429
430
  }
430
431
  try {
431
- return {
432
- html: we(E.toHtmlString()),
433
- ...T ? { sourceMap: T } : {}
432
+ let p = ge(E.toHtmlString());
433
+ return e.theme && (p = Q(p, e.theme)), {
434
+ html: p,
435
+ ...I ? { sourceMap: I } : {}
434
436
  };
435
437
  } catch (p) {
436
438
  throw C(p), p;
@@ -438,7 +440,7 @@ async function ve(e) {
438
440
  E.delete();
439
441
  }
440
442
  } finally {
441
- re(s);
443
+ ne(s);
442
444
  }
443
445
  }
444
446
  const O = `Usage: pretext-html <source.ptx> [options]
@@ -467,7 +469,7 @@ Options:
467
469
  file/line for every element (for editor sync)
468
470
  -h, --help Show this help
469
471
  `;
470
- function $e(e) {
472
+ function Ee(e) {
471
473
  let r, t, n, o;
472
474
  const i = {}, l = {};
473
475
  for (let s = 0; s < e.length; s++) {
@@ -532,22 +534,22 @@ function $e(e) {
532
534
  sourceMapPath: o
533
535
  };
534
536
  }
535
- async function Te(e) {
536
- const { options: r, output: t, docinfoPath: n, sourceMapPath: o } = $e(e);
537
+ async function je(e) {
538
+ const { options: r, output: t, docinfoPath: n, sourceMapPath: o } = Ee(e);
537
539
  n && (r.docinfo = await m(n, "utf8"));
538
- const i = Date.now(), { html: l, sourceMap: s } = await ve(r);
540
+ const i = Date.now(), { html: l, sourceMap: s } = await $e(r);
539
541
  w.stderr.write(`pretext-html: rendered in ${Date.now() - i}ms
540
- `), o && s && (await j(o, JSON.stringify(s, null, 2)), w.stderr.write(`pretext-html: wrote ${o}
541
- `)), t ? (await j(t, l), w.stderr.write(`pretext-html: wrote ${t}
542
+ `), o && s && (await X(o, JSON.stringify(s, null, 2)), w.stderr.write(`pretext-html: wrote ${o}
543
+ `)), t ? (await X(t, l), w.stderr.write(`pretext-html: wrote ${t}
542
544
  `)) : w.stdout.write(l);
543
545
  }
544
546
  export {
545
547
  L as a,
546
- de as d,
547
- Ie as f,
548
- he as i,
549
- Te as m,
550
- $e as p,
551
- ve as r,
548
+ pe as d,
549
+ De as f,
550
+ me as i,
551
+ je as m,
552
+ Ee as p,
553
+ $e as r,
552
554
  U as x
553
555
  };
package/dist/cli.d.ts CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "node:fs/promises";
2
2
  import "node:process";
3
- import { m as o, p as s } from "./cli-C6tfAVvi.js";
3
+ import { m as o, p as s } from "./cli-BQizBK0N.js";
4
4
  export {
5
5
  o as main,
6
6
  s as parseArgs
package/dist/index.d.ts CHANGED
@@ -3,4 +3,6 @@ export type { RenderOptions, RenderResult } from './renderer.js';
3
3
  export { findSourceMapEntry } from './sourcemap.js';
4
4
  export type { PtxSourceMap, SourceMapEntry } from './sourcemap.js';
5
5
  export { forcePortablePublication } from './publication.js';
6
+ export { PREVIEW_THEME_MESSAGE, isPreviewTheme, previewThemeMessage, themeBridgeScript, injectThemeBridge, } from './theme.js';
7
+ export type { PreviewTheme, PreviewThemeMessage } from './theme.js';
6
8
  export { main as runCli } from './cli.js';
package/dist/index.js CHANGED
@@ -1,10 +1,16 @@
1
- import { d as i, f as s, a as e, i as l, r as t, m as n, x as o } from "./cli-C6tfAVvi.js";
1
+ import { d as i, f as a, a as s, i as t, r as l, m, x as o } from "./cli-BQizBK0N.js";
2
+ import { PREVIEW_THEME_MESSAGE as p, injectThemeBridge as E, isPreviewTheme as d, previewThemeMessage as f, themeBridgeScript as c } from "./theme.js";
2
3
  export {
4
+ p as PREVIEW_THEME_MESSAGE,
3
5
  i as defaultXslDir,
4
- s as findSourceMapEntry,
5
- e as forcePortablePublication,
6
- l as isJspiAvailable,
7
- t as renderHtml,
8
- n as runCli,
6
+ a as findSourceMapEntry,
7
+ s as forcePortablePublication,
8
+ E as injectThemeBridge,
9
+ t as isJspiAvailable,
10
+ d as isPreviewTheme,
11
+ f as previewThemeMessage,
12
+ l as renderHtml,
13
+ m as runCli,
14
+ c as themeBridgeScript,
9
15
  o as xpathStringLiteral
10
16
  };
package/dist/mounts.d.ts CHANGED
File without changes
File without changes
@@ -1,4 +1,5 @@
1
1
  import { PtxSourceMap } from './sourcemap.js';
2
+ import { PreviewTheme } from './theme.js';
2
3
  export interface RenderOptions {
3
4
  /** Path to the root PreTeXt source file (typically source/main.ptx). */
4
5
  sourcePath: string;
@@ -61,6 +62,15 @@ export interface RenderOptions {
61
62
  * sourcemap.ts for the id contract with pretext-assembly.xsl.
62
63
  */
63
64
  sourceMap?: boolean;
65
+ /**
66
+ * Let the embedding app control the preview's light/dark theme. When set, a
67
+ * small bridge script is injected into the page that applies this value as
68
+ * the initial theme and then follows `postMessage`s from the embedder (see
69
+ * `previewThemeMessage` / theme.ts). Omit to leave the page's native
70
+ * behaviour (localStorage + `prefers-color-scheme`) untouched — the output
71
+ * is then byte-identical to a render without this option.
72
+ */
73
+ theme?: PreviewTheme;
64
74
  }
65
75
  export interface RenderResult {
66
76
  /** Complete standalone HTML page (CDN-hosted css/js/MathJax). */
File without changes
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Runtime light/dark theme control for rendered previews.
3
+ *
4
+ * A rendered PreTeXt page already knows how to switch between light and dark
5
+ * — pretext-core.js exposes a global `window.setDarkMode(isDark)` — but it
6
+ * only ever decides *which* to use from `localStorage` and the browser's
7
+ * `prefers-color-scheme`. Neither reflects the surroundings an embedder cares
8
+ * about: "VS Code is using a dark editor theme", "pretext.plus is in dark
9
+ * mode". This module lets the embedding app drive the preview's theme instead.
10
+ *
11
+ * It is entirely opt-in, controlled by `RenderOptions.theme`. When set, the
12
+ * renderer injects the small script produced by {@link themeBridgeScript},
13
+ * which:
14
+ *
15
+ * 1. applies an *initial* theme baked into the page (so an explicit dark
16
+ * preview does not flash light first), and
17
+ * 2. listens for a `postMessage` from the embedder and re-applies the theme
18
+ * live — no re-render needed.
19
+ *
20
+ * The embedder posts {@link previewThemeMessage}`(theme)` to the preview's
21
+ * window (a VS Code webview, an `<iframe>`'s `contentWindow`, or the page's
22
+ * own `window` for an inline render). This module has no runtime dependencies,
23
+ * so an embedder can import just the protocol (`@pretextbook/pretext-html/theme`)
24
+ * without pulling in the WASM renderer.
25
+ *
26
+ * Timing note: `window.setDarkMode` is defined by pretext-core.js only inside
27
+ * its `DOMContentLoaded` handler, so the injected script cannot assume it
28
+ * exists yet — it retries on `DOMContentLoaded`/`load` and remembers the last
29
+ * requested theme on `window` so it survives the extension's in-place
30
+ * `document.write` rebuilds.
31
+ */
32
+ export type PreviewTheme = "dark" | "light" | "system";
33
+ /**
34
+ * The `type` field of the message an embedder posts to drive the preview's
35
+ * theme. Namespaced so it does not collide with an embedder's own messages.
36
+ */
37
+ export declare const PREVIEW_THEME_MESSAGE = "pretext-html:set-theme";
38
+ export interface PreviewThemeMessage {
39
+ type: typeof PREVIEW_THEME_MESSAGE;
40
+ theme: PreviewTheme;
41
+ }
42
+ /** True when `value` is one of the three {@link PreviewTheme} strings. */
43
+ export declare function isPreviewTheme(value: unknown): value is PreviewTheme;
44
+ /**
45
+ * Build the message an embedder posts to a rendered preview to set its theme.
46
+ * Send it with `webview.postMessage(...)` (VS Code), `iframe.contentWindow
47
+ * .postMessage(..., "*")` (embedded), or `window.postMessage(...)` (inline).
48
+ */
49
+ export declare function previewThemeMessage(theme: PreviewTheme): PreviewThemeMessage;
50
+ /**
51
+ * The inline `<script>` (as a string) that wires a rendered page to the
52
+ * embedder's theme. Injected into the page `<head>` by the renderer when
53
+ * `RenderOptions.theme` is set; `initial` is baked in as the starting theme.
54
+ *
55
+ * Written in ES5-ish, self-contained JS because it is embedded verbatim in the
56
+ * output page (no bundler runs over it) and re-executes on every in-place
57
+ * `document.write` rebuild. It is idempotent: handlers are keyed on `window`
58
+ * and removed before being re-added, and the last requested theme persists on
59
+ * `window` so a rebuilt document re-applies it rather than snapping back to
60
+ * `initial`.
61
+ */
62
+ export declare function themeBridgeScript(initial: PreviewTheme): string;
63
+ /**
64
+ * Inject the theme bridge script into a rendered HTML page's `<head>` (as
65
+ * early as possible, to minimise any flash). Returns the page unchanged when
66
+ * it has no recognisable `<head>`.
67
+ */
68
+ export declare function injectThemeBridge(html: string, theme: PreviewTheme): string;
package/dist/theme.js ADDED
@@ -0,0 +1,93 @@
1
+ const n = "pretext-html:set-theme";
2
+ function i(e) {
3
+ return e === "dark" || e === "light" || e === "system";
4
+ }
5
+ function d(e) {
6
+ return { type: n, theme: e };
7
+ }
8
+ function a(e) {
9
+ return [
10
+ "<script>",
11
+ "(function () {",
12
+ " var CFG = " + JSON.stringify({ msg: n, initial: e }) + ";",
13
+ // Where the last requested theme is remembered so it survives an in-place
14
+ // document rewrite (same Window) and outranks the baked initial value.
15
+ " var KEY = '__ptxPreviewTheme';",
16
+ " var MEDIA = '(prefers-color-scheme: dark)';",
17
+ " function isTheme(v) {",
18
+ " return v === 'dark' || v === 'light' || v === 'system';",
19
+ " }",
20
+ " function prefersDark() {",
21
+ " return !!(window.matchMedia && window.matchMedia(MEDIA).matches);",
22
+ " }",
23
+ " function resolveDark(theme) {",
24
+ " if (theme === 'dark') return true;",
25
+ " if (theme === 'light') return false;",
26
+ " return prefersDark();",
27
+ // "system"
28
+ " }",
29
+ " function current() {",
30
+ " return isTheme(window[KEY]) ? window[KEY] : CFG.initial;",
31
+ " }",
32
+ // A theme with no dark variant marks the root data-darkmode="disabled";
33
+ // never force dark mode on in that case (mirrors setDarkMode's own guard).
34
+ " function darkModeDisabled() {",
35
+ " var el = document.documentElement;",
36
+ " return !!(el && el.dataset && el.dataset.darkmode === 'disabled');",
37
+ " }",
38
+ " function apply(theme) {",
39
+ " window[KEY] = theme;",
40
+ " if (darkModeDisabled()) return;",
41
+ " var dark = resolveDark(theme);",
42
+ // Toggle the class setDarkMode would set, right now, so an explicit theme
43
+ // does not flash before pretext-core.js defines setDarkMode. setDarkMode
44
+ // (once available) then does the complete job, including iframes.
45
+ " var el = document.documentElement;",
46
+ " if (el && el.classList) { el.classList.toggle('dark-mode', dark); }",
47
+ " if (typeof window.setDarkMode === 'function') { window.setDarkMode(dark); }",
48
+ " }",
49
+ " function reapply() { apply(current()); }",
50
+ // Immediately (no-op until setDarkMode exists), then again once the page's
51
+ // own script has defined it.
52
+ " reapply();",
53
+ " document.addEventListener('DOMContentLoaded', reapply);",
54
+ " window.addEventListener('load', reapply);",
55
+ // Embedder -> preview live updates.
56
+ " if (window.__ptxThemeMsgHandler) {",
57
+ " window.removeEventListener('message', window.__ptxThemeMsgHandler);",
58
+ " }",
59
+ " window.__ptxThemeMsgHandler = function (event) {",
60
+ " var data = event && event.data;",
61
+ " if (data && data.type === CFG.msg && isTheme(data.theme)) {",
62
+ " apply(data.theme);",
63
+ " }",
64
+ " };",
65
+ " window.addEventListener('message', window.__ptxThemeMsgHandler);",
66
+ // Track OS changes while following the system theme.
67
+ " if (window.matchMedia) {",
68
+ " var mq = window.matchMedia(MEDIA);",
69
+ " if (window.__ptxThemeMediaHandler) {",
70
+ " try { mq.removeEventListener('change', window.__ptxThemeMediaHandler); } catch (e) {}",
71
+ " }",
72
+ " window.__ptxThemeMediaHandler = function () {",
73
+ " if (current() === 'system') { reapply(); }",
74
+ " };",
75
+ " try { mq.addEventListener('change', window.__ptxThemeMediaHandler); } catch (e) {}",
76
+ " }",
77
+ "})();",
78
+ "<\/script>"
79
+ ].join(`
80
+ `);
81
+ }
82
+ function o(e, t) {
83
+ const r = a(t);
84
+ return /<head[^>]*>/i.test(e) ? e.replace(/<head([^>]*)>/i, `<head$1>
85
+ ${r}`) : e;
86
+ }
87
+ export {
88
+ n as PREVIEW_THEME_MESSAGE,
89
+ o as injectThemeBridge,
90
+ i as isPreviewTheme,
91
+ d as previewThemeMessage,
92
+ a as themeBridgeScript
93
+ };
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretextbook/pretext-html",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Convert PreTeXt documents to HTML in pure JavaScript using the official PreTeXt XSLT stylesheets via libxslt-wasm. No Python or PreTeXt installation required.",
5
5
  "author": "Oscar Levin",
6
6
  "license": "MIT",
@@ -16,6 +16,13 @@
16
16
  "type": "module",
17
17
  "main": "./dist/index.js",
18
18
  "types": "./dist/index.d.ts",
19
+ "typesVersions": {
20
+ "*": {
21
+ "theme": [
22
+ "./dist/theme.d.ts"
23
+ ]
24
+ }
25
+ },
19
26
  "bin": {
20
27
  "pretext-html": "./cli.mjs"
21
28
  },
@@ -25,6 +32,11 @@
25
32
  "import": "./dist/index.js",
26
33
  "default": "./dist/index.js"
27
34
  },
35
+ "./theme": {
36
+ "types": "./dist/theme.d.ts",
37
+ "import": "./dist/theme.js",
38
+ "default": "./dist/theme.js"
39
+ },
28
40
  "./cli": "./dist/cli.js"
29
41
  },
30
42
  "sideEffects": false,
@@ -44,7 +56,7 @@
44
56
  "xast-util-to-xml": "^4.0.0"
45
57
  },
46
58
  "engines": {
47
- "node": ">=22"
59
+ "node": ">=24"
48
60
  },
49
61
  "publishConfig": {
50
62
  "access": "public"