@pretextbook/pretext-html 0.1.0 → 0.2.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 (106) hide show
  1. package/README.md +104 -4
  2. package/assets/preview-html.xsl +230 -230
  3. package/assets/upstream.json +7 -7
  4. package/assets/xsl/entities.ent +154 -154
  5. package/assets/xsl/extract-asymptote.xsl +68 -68
  6. package/assets/xsl/extract-biblio-csl.xsl +327 -327
  7. package/assets/xsl/extract-datafile.xsl +71 -71
  8. package/assets/xsl/extract-dynamic.xsl +139 -139
  9. package/assets/xsl/extract-identity.xsl +136 -136
  10. package/assets/xsl/extract-interactive.xsl +81 -81
  11. package/assets/xsl/extract-latex-image.xsl +118 -118
  12. package/assets/xsl/extract-mermaid.xsl +58 -58
  13. package/assets/xsl/extract-mom.xsl +63 -63
  14. package/assets/xsl/extract-pg.xsl +2897 -2897
  15. package/assets/xsl/extract-prefigure.xsl +157 -157
  16. package/assets/xsl/extract-qrcode.xsl +100 -100
  17. package/assets/xsl/extract-runestone-page-template.xsl +139 -139
  18. package/assets/xsl/extract-sageplot.xsl +166 -166
  19. package/assets/xsl/extract-stack.xsl +56 -56
  20. package/assets/xsl/extract-trace.xsl +134 -134
  21. package/assets/xsl/extract-youtube.xsl +63 -63
  22. package/assets/xsl/html-symbols.xsl +3344 -3344
  23. package/assets/xsl/latex/pretext-latex-AIM.xsl +56 -56
  24. package/assets/xsl/latex/pretext-latex-CLP.xsl +148 -148
  25. package/assets/xsl/latex/pretext-latex-chaos.xsl +317 -317
  26. package/assets/xsl/latex/pretext-latex-dyslexic-font.xsl +76 -76
  27. package/assets/xsl/latex/pretext-latex-guide.xsl +104 -104
  28. package/assets/xsl/latex/pretext-latex-texstyle.xsl +862 -862
  29. package/assets/xsl/localizations/af-ZA.xml +276 -276
  30. package/assets/xsl/localizations/bg-BG.xml +276 -276
  31. package/assets/xsl/localizations/ca-ES.xml +285 -285
  32. package/assets/xsl/localizations/cs-CZ.xml +280 -280
  33. package/assets/xsl/localizations/de-DE.xml +281 -281
  34. package/assets/xsl/localizations/en-US.xml +427 -427
  35. package/assets/xsl/localizations/es-ES.xml +287 -287
  36. package/assets/xsl/localizations/fi-FI.xml +275 -275
  37. package/assets/xsl/localizations/fr-CA.xml +276 -276
  38. package/assets/xsl/localizations/fr-FR.xml +280 -280
  39. package/assets/xsl/localizations/hu-HU.xml +280 -280
  40. package/assets/xsl/localizations/it-IT.xml +276 -276
  41. package/assets/xsl/localizations/ku-CKB.xml +275 -275
  42. package/assets/xsl/localizations/localizations.xml +48 -48
  43. package/assets/xsl/localizations/nl-NL.xml +275 -275
  44. package/assets/xsl/localizations/pt-BR.xml +288 -288
  45. package/assets/xsl/localizations/pt-PT.xml +292 -292
  46. package/assets/xsl/localizations/zh-HANS.xml +269 -269
  47. package/assets/xsl/pretext-assembly.xsl +4795 -4795
  48. package/assets/xsl/pretext-basic-html.xsl +86 -86
  49. package/assets/xsl/pretext-beamer.xsl +662 -662
  50. package/assets/xsl/pretext-braille-preprint.xsl +2249 -2249
  51. package/assets/xsl/pretext-common.xsl +12018 -12018
  52. package/assets/xsl/pretext-dynamic.xsl +65 -65
  53. package/assets/xsl/pretext-epub.xsl +1483 -1483
  54. package/assets/xsl/pretext-fo.xsl +4593 -4593
  55. package/assets/xsl/pretext-html.xsl +14791 -14791
  56. package/assets/xsl/pretext-json-manifest.xsl +150 -150
  57. package/assets/xsl/pretext-jupyter.xsl +868 -868
  58. package/assets/xsl/pretext-latex-classic.xsl +952 -952
  59. package/assets/xsl/pretext-latex-common.xsl +11563 -11563
  60. package/assets/xsl/pretext-latex.xsl +47 -47
  61. package/assets/xsl/pretext-litprog.xsl +105 -105
  62. package/assets/xsl/pretext-merge.xsl +41 -41
  63. package/assets/xsl/pretext-numbers.xsl +875 -875
  64. package/assets/xsl/pretext-revealjs.xsl +522 -522
  65. package/assets/xsl/pretext-runestone-fitb.xsl +1233 -1233
  66. package/assets/xsl/pretext-runestone-static.xsl +1438 -1438
  67. package/assets/xsl/pretext-runestone.xsl +3083 -3083
  68. package/assets/xsl/pretext-sage-doctest.xsl +289 -289
  69. package/assets/xsl/pretext-smc.xsl +492 -492
  70. package/assets/xsl/pretext-solution-manual-latex.xsl +323 -323
  71. package/assets/xsl/pretext-text-utilities.xsl +1217 -1217
  72. package/assets/xsl/pretext-text.xsl +392 -392
  73. package/assets/xsl/pretext-units.xsl +195 -195
  74. package/assets/xsl/pretext-view-source.xsl +157 -157
  75. package/assets/xsl/pretext-ww-problem-sets.xsl +391 -391
  76. package/assets/xsl/publisher-variables.xsl +4756 -4756
  77. package/assets/xsl/support/extract-math.xsl +256 -256
  78. package/assets/xsl/support/package-math.xsl +141 -141
  79. package/assets/xsl/support/play-button/play-button.svg +3 -3
  80. package/assets/xsl/support/pretext-pg-macros.xsl +110 -110
  81. package/assets/xsl/support/webpack_static_imports.xml +14 -14
  82. package/assets/xsl/tests/null.xml +3 -3
  83. package/assets/xsl/tests/pretext-text-utilities-test.xsl +333 -333
  84. package/assets/xsl/utilities/author-report.xsl +107 -107
  85. package/assets/xsl/utilities/deprecate-autoname.sed +18 -18
  86. package/assets/xsl/utilities/deprecate-index.sed +33 -33
  87. package/assets/xsl/utilities/fix-deprecations.xsl +663 -663
  88. package/assets/xsl/utilities/pretext-enhanced-source.xsl +112 -112
  89. package/assets/xsl/utilities/report-publisher-variables.xsl +162 -162
  90. package/assets/xsl/xml-to-json.xsl +326 -326
  91. package/assets/xsl/xml-to-string.xsl +328 -328
  92. package/assets/xsl-bundle.json +1 -0
  93. package/dist/{cli-C6tfAVvi.js → cli-B2LiKunq.js} +268 -233
  94. package/dist/cli.js +1 -1
  95. package/dist/host.browser.d.ts +29 -0
  96. package/dist/host.d.ts +41 -0
  97. package/dist/index.browser.d.ts +29 -0
  98. package/dist/index.browser.js +650 -0
  99. package/dist/index.d.ts +5 -0
  100. package/dist/index.js +16 -8
  101. package/dist/internal/posix-path.d.ts +59 -0
  102. package/dist/mounts.d.ts +18 -0
  103. package/dist/renderer.d.ts +14 -1
  104. package/dist/theme.d.ts +68 -0
  105. package/dist/theme.js +93 -0
  106. package/package.json +22 -4
@@ -0,0 +1,59 @@
1
+ /**
2
+ * A posix-only stand-in for `node:path`, substituted for it when this package
3
+ * is built for the browser (see vite.config.ts).
4
+ *
5
+ * This is safe because none of the paths this package computes ever reach a
6
+ * real filesystem: libxslt-wasm is compiled with FILESYSTEM=0, so every path
7
+ * ends up as either a key in the mount table or a segment of a
8
+ * `*.ptx.invalid` URL (see mounts.ts). They are virtual paths, and virtual
9
+ * paths are posix paths — a browser caller passes `sourcePath: "/main.ptx"`
10
+ * and means exactly what it says.
11
+ *
12
+ * Only the handful of functions this package actually uses are implemented.
13
+ */
14
+ export declare const sep = "/";
15
+ /**
16
+ * Collapse `.` and `..` segments. An absolute path clamps at the root
17
+ * (`/a/../../b` → `/b`), which is what makes the containment checks in
18
+ * mounts.ts and xinclude.ts safe against `..` traversal.
19
+ */
20
+ export declare function normalize(p: string): string;
21
+ /**
22
+ * Resolve right-to-left until an absolute path is found. With no absolute
23
+ * segment the result is rooted at `/` rather than at a process cwd — there is
24
+ * no cwd in a browser, and a virtual path is meaningless relative to one.
25
+ */
26
+ export declare function resolve(...segments: string[]): string;
27
+ export declare function join(...segments: string[]): string;
28
+ export declare function dirname(p: string): string;
29
+ export declare function relative(from: string, to: string): string;
30
+ /**
31
+ * `path.posix` self-reference: on posix, `path` and `path.posix` are the same
32
+ * thing, and call sites that reach for `path.posix.normalize` to be explicit
33
+ * should keep working.
34
+ */
35
+ export declare const posix: {
36
+ sep: string;
37
+ normalize: typeof normalize;
38
+ resolve: typeof resolve;
39
+ join: typeof join;
40
+ dirname: typeof dirname;
41
+ relative: typeof relative;
42
+ };
43
+ declare const _default: {
44
+ sep: string;
45
+ normalize: typeof normalize;
46
+ resolve: typeof resolve;
47
+ join: typeof join;
48
+ dirname: typeof dirname;
49
+ relative: typeof relative;
50
+ posix: {
51
+ sep: string;
52
+ normalize: typeof normalize;
53
+ resolve: typeof resolve;
54
+ join: typeof join;
55
+ dirname: typeof dirname;
56
+ relative: typeof relative;
57
+ };
58
+ };
59
+ export default _default;
package/dist/mounts.d.ts CHANGED
@@ -10,11 +10,29 @@
10
10
  * reserved `.invalid` TLD and serve them from local directories or in-memory
11
11
  * strings. No HTTP server, no real network traffic.
12
12
  */
13
+ /**
14
+ * Reads `relPath` (posix, leading "/") beneath mount root `root`, or resolves
15
+ * `undefined` when it is missing or escapes the mount. `root` is opaque: a
16
+ * directory under Node, a base URL in a browser, whatever an embedder's own
17
+ * reader wants.
18
+ */
19
+ export type MountReader = (root: string, relPath: string) => Promise<Uint8Array | string | undefined>;
20
+ /**
21
+ * Replace how mounted files are read. Lets an embedder serve the stylesheets
22
+ * from somewhere the platform seam does not cover — an in-memory map, a zip,
23
+ * a VS Code webview resource URI — without patching `globalThis.fetch`.
24
+ * Also how scripts/build-xsl-bundle.mjs records which files a render touches.
25
+ */
26
+ export declare function setMountReader(reader: MountReader): void;
13
27
  /** Install the shim (idempotent). Must run before libxslt-wasm loads anything. */
14
28
  export declare function installFetchShim(): void;
15
29
  /**
16
30
  * Serve `dir` under a unique fake origin. Returns the base URL
17
31
  * (e.g. `http://mnt1.ptx.invalid`); resource paths append to it.
32
+ *
33
+ * `dir` is stored verbatim and handed back to the {@link MountReader}, which
34
+ * owns its interpretation — under Node a directory path, in a browser a base
35
+ * URL.
18
36
  */
19
37
  export declare function mountDirectory(dir: string): string;
20
38
  export declare function unmountDirectory(baseUrl: string): void;
@@ -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). */
@@ -68,7 +78,10 @@ export interface RenderResult {
68
78
  /** Present when RenderOptions.sourceMap was set. */
69
79
  sourceMap?: PtxSourceMap;
70
80
  }
71
- /** Vendored stylesheets shipped with this package. */
81
+ /**
82
+ * Vendored stylesheets shipped with this package. A directory under Node, a
83
+ * base URL in the browser build — either way, a mount root (see host.ts).
84
+ */
72
85
  export declare function defaultXslDir(): string;
73
86
  export declare function isJspiAvailable(): boolean;
74
87
  /** Quote a string as an XPath 1.0 string literal. */
@@ -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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretextbook/pretext-html",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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,15 +16,31 @@
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
  },
22
29
  "exports": {
23
30
  ".": {
31
+ "browser": {
32
+ "types": "./dist/index.browser.d.ts",
33
+ "default": "./dist/index.browser.js"
34
+ },
24
35
  "types": "./dist/index.d.ts",
25
36
  "import": "./dist/index.js",
26
37
  "default": "./dist/index.js"
27
38
  },
39
+ "./theme": {
40
+ "types": "./dist/theme.d.ts",
41
+ "import": "./dist/theme.js",
42
+ "default": "./dist/theme.js"
43
+ },
28
44
  "./cli": "./dist/cli.js"
29
45
  },
30
46
  "sideEffects": false,
@@ -34,9 +50,11 @@
34
50
  "cli.mjs"
35
51
  ],
36
52
  "scripts": {
37
- "build": "vite build",
53
+ "build": "vite build && npm run build:browser && npm run build-xsl-bundle",
54
+ "build:browser": "vite build --mode browser",
38
55
  "test": "vitest run",
39
- "refresh-xsl": "node scripts/refresh-xsl.mjs"
56
+ "refresh-xsl": "node scripts/refresh-xsl.mjs",
57
+ "build-xsl-bundle": "node --experimental-wasm-jspi scripts/build-xsl-bundle.mjs"
40
58
  },
41
59
  "dependencies": {
42
60
  "@pretextbook/libxslt-wasm": "^0.1.1",
@@ -44,7 +62,7 @@
44
62
  "xast-util-to-xml": "^4.0.0"
45
63
  },
46
64
  "engines": {
47
- "node": ">=22"
65
+ "node": ">=24"
48
66
  },
49
67
  "publishConfig": {
50
68
  "access": "public"