@pretextbook/pretext-html 0.10.0 → 0.11.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.
package/dist/index.d.ts CHANGED
@@ -22,6 +22,8 @@ export { PREVIEW_THEME_MESSAGE, isPreviewTheme, previewThemeMessage, themeBridge
22
22
  export type { PreviewTheme, PreviewThemeMessage } from './theme.js';
23
23
  export { PREVIEW_BANNER_STORAGE_KEY, injectPreviewBanner, previewBannerScript, } from './banner.js';
24
24
  export type { PreviewBannerOptions } from './banner.js';
25
+ export { PRINT_PREVIEW_GLOBAL, injectPrintPreview, listPrintouts, printPreviewBridgeScript, rootPrintoutId, } from './printout.js';
26
+ export type { PrintoutInfo } from './printout.js';
25
27
  export { main as runCli } from './cli.js';
26
28
  export { setAssetsBase } from './host.js';
27
29
  export { setMountReader } from './mounts.js';
package/dist/index.js CHANGED
@@ -1,37 +1,43 @@
1
- import { O as r, P as i, b as a, d as t, a as n, c as o, f as l, e as d, i as p, g as E, o as c, p as m, r as v, h as S, j as g, m as A, s as f, k as B, x as P } from "./cli-BONFCRgl.js";
2
- import { injectRevealBridge as R, isRevealView as h, revealBridgeScript as _ } from "./reveal.js";
3
- import { describeAsset as u, missingAssetPlaceholder as x, missingAssetSvg as M, rewriteAssetUrls as T } from "./assets.js";
4
- import { PREVIEW_THEME_MESSAGE as G, injectThemeBridge as k, isPreviewTheme as H, previewThemeMessage as L, themeBridgeScript as O } from "./theme.js";
1
+ import { O as i, P as s, b as t, d as a, a as n, c as o, f as l, e as d, i as p, g as E, o as c, p as P, r as v, h as m, j as S, m as g, s as A, k as B, x as f } from "./cli-CMAunrD_.js";
2
+ import { injectRevealBridge as R, isRevealView as _, revealBridgeScript as u } from "./reveal.js";
3
+ import { describeAsset as x, missingAssetPlaceholder as T, missingAssetSvg as b, rewriteAssetUrls as M } from "./assets.js";
4
+ import { PREVIEW_THEME_MESSAGE as G, injectThemeBridge as I, isPreviewTheme as L, previewThemeMessage as O, themeBridgeScript as V } from "./theme.js";
5
+ import { PRINT_PREVIEW_GLOBAL as H, injectPrintPreview as N, listPrintouts as W, printPreviewBridgeScript as D, rootPrintoutId as F } from "./printout.js";
5
6
  export {
6
- r as OFF_PAGE_MESSAGE,
7
- i as PREVIEW_BANNER_STORAGE_KEY,
7
+ i as OFF_PAGE_MESSAGE,
8
+ s as PREVIEW_BANNER_STORAGE_KEY,
8
9
  G as PREVIEW_THEME_MESSAGE,
9
- a as buildSkeleton,
10
- t as defaultXslDir,
11
- u as describeAsset,
10
+ H as PRINT_PREVIEW_GLOBAL,
11
+ t as buildSkeleton,
12
+ a as defaultXslDir,
13
+ x as describeAsset,
12
14
  n as detectRenderTarget,
13
15
  o as divisionLevel,
14
16
  l as findSourceMapEntry,
15
17
  d as forcePortablePublication,
16
18
  p as injectPreviewBanner,
19
+ N as injectPrintPreview,
17
20
  R as injectRevealBridge,
18
- k as injectThemeBridge,
21
+ I as injectThemeBridge,
19
22
  E as isJspiAvailable,
20
- H as isPreviewTheme,
21
- h as isRevealView,
22
- x as missingAssetPlaceholder,
23
- M as missingAssetSvg,
23
+ L as isPreviewTheme,
24
+ _ as isRevealView,
25
+ W as listPrintouts,
26
+ T as missingAssetPlaceholder,
27
+ b as missingAssetSvg,
24
28
  c as openBornHiddenKnowls,
25
- m as previewBannerScript,
26
- L as previewThemeMessage,
29
+ P as previewBannerScript,
30
+ O as previewThemeMessage,
31
+ D as printPreviewBridgeScript,
27
32
  v as renderHtml,
28
- S as replaceDivision,
29
- _ as revealBridgeScript,
30
- T as rewriteAssetUrls,
31
- g as rewriteXrefLinks,
32
- A as runCli,
33
- f as setAssetsBase,
33
+ m as replaceDivision,
34
+ u as revealBridgeScript,
35
+ M as rewriteAssetUrls,
36
+ S as rewriteXrefLinks,
37
+ F as rootPrintoutId,
38
+ g as runCli,
39
+ A as setAssetsBase,
34
40
  B as setMountReader,
35
- O as themeBridgeScript,
36
- P as xpathStringLiteral
41
+ V as themeBridgeScript,
42
+ f as xpathStringLiteral
37
43
  };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Print-preview layout for the printouts on a rendered page.
3
+ *
4
+ * A worksheet or handout is written to be printed and handed out, and PreTeXt
5
+ * lays one out very differently on paper than on screen: paginated to the
6
+ * chosen paper size, headers and footers on each sheet, solutions dropped, and
7
+ * — the part that is impossible to judge from the ordinary view — every
8
+ * `<workspace>` grown to the height the author asked for, so the blank space a
9
+ * student writes in is actually shown. An author editing a worksheet wants to
10
+ * see *that*, not the web version of it.
11
+ *
12
+ * ## How the built page does it, and why the preview cannot
13
+ *
14
+ * All of it is client-side, in pretext-core.js, and the entire entry point is a
15
+ * URL query parameter: `?printpreview=<html-id>`. On DOMContentLoaded the
16
+ * script swaps the theme stylesheet for `print-worksheet.css`, moves the named
17
+ * printout to be the page's only content, flattens its knowls, and paginates
18
+ * it. Upstream's print icon on a printout heading is nothing more than a link
19
+ * to that URL.
20
+ *
21
+ * A preview has no such URL. It is rendered in memory and handed to a host — a
22
+ * VS Code webview, an iframe on pretext.plus — so there is no page to reload
23
+ * and no query string of ours to put the parameter in; the link is disabled in
24
+ * the preview wrapper for exactly that reason (see PRINTOUT_LINK_OVERRIDE in
25
+ * scripts/refresh-xsl.mjs). What this module does is supply the parameter
26
+ * without the navigation: {@link injectPrintPreview} adds a small script that
27
+ * makes `new URLSearchParams(window.location.search)` — the one and only place
28
+ * pretext-core.js looks — report a `printpreview` parameter that is not in the
29
+ * URL at all.
30
+ *
31
+ * Rewriting the URL itself would be the obvious alternative and does not work
32
+ * here: `history.replaceState` throws in a document whose origin cannot own a
33
+ * new URL (a `vscode-webview://` page, an `about:blank` document written into
34
+ * in place), and on a host like pretext.plus it would corrupt the app's own
35
+ * address. Patching the reader is inert by comparison — it adds a parameter to
36
+ * one particular string and touches nothing else.
37
+ *
38
+ * ## Switching in and out
39
+ *
40
+ * By re-delivering the page, not by talking to a live one, exactly as the
41
+ * slideshow view toggle does (see reveal.ts). pretext-core.js runs the whole
42
+ * transformation once, from a DOMContentLoaded handler, and it is thoroughly
43
+ * destructive — sections are removed from the DOM, knowls are flattened,
44
+ * `<details>` become `<div>`s. There is no undo. Since the embedder already
45
+ * holds the rendered HTML, leaving print preview means re-injecting the *off*
46
+ * state into that pristine HTML and delivering it again; a fresh document
47
+ * (whether from a reload or an in-place `document.write`) fires
48
+ * DOMContentLoaded again and the page comes up in the other mode. No re-render.
49
+ *
50
+ * Injecting the off state matters as much as injecting the on state: a
51
+ * `document.write` rebuild reuses the same `Window`, so a patch installed by an
52
+ * earlier delivery is still in place. The script therefore keeps the wanted id
53
+ * in a window property it re-reads on every call, and the off state clears it.
54
+ */
55
+ /** A printout on a rendered page: a division the reader can lay out for print. */
56
+ export interface PrintoutInfo {
57
+ /**
58
+ * The printout element's HTML id — what `?printpreview=` names, and what
59
+ * {@link injectPrintPreview} takes.
60
+ */
61
+ id: string;
62
+ /** Localized division type: "Worksheet", "Handout", "Project", … */
63
+ type: string;
64
+ /** The printout's number as printed ("2.3"), or "" when it has none. */
65
+ number: string;
66
+ /** The printout's title as plain text, or "" when it is untitled. */
67
+ title: string;
68
+ /**
69
+ * The three above composed into one line — "Worksheet 2.3: Counting" — for
70
+ * an embedder that just wants to put the page's printouts in a menu.
71
+ */
72
+ label: string;
73
+ }
74
+ /**
75
+ * The printouts on a rendered page, in document order.
76
+ *
77
+ * Empty for a page with none, which is the signal an embedder wants: with no
78
+ * printouts there is nothing to offer, and — because PreTeXt only emits the
79
+ * paper-size and print-options controls on a page that has one — no print
80
+ * preview to be had either.
81
+ */
82
+ export declare function listPrintouts(html: string): PrintoutInfo[];
83
+ /**
84
+ * The printout to open in print preview when a document is *itself* a printout
85
+ * — the case of previewing `worksheet-3.ptx`, whose root element is the
86
+ * `<worksheet>`. Nothing else in the file competes for the reader's attention,
87
+ * so the paper layout is what they came to see.
88
+ *
89
+ * `rootElement` is the document's root element name **before** any fragment
90
+ * wrapping: a lone worksheet is rendered inside a synthesized `<article>`, and
91
+ * by the time the page exists that is all the HTML can say. The printout is the
92
+ * page's first, since the wrapper holds nothing else.
93
+ *
94
+ * Undefined for a document that merely contains printouts — a chapter with
95
+ * three worksheets in it should open as the chapter.
96
+ */
97
+ export declare function rootPrintoutId(printouts: PrintoutInfo[], rootElement: string | undefined): string | undefined;
98
+ /**
99
+ * Window property holding the id the patched reader should report. Read on
100
+ * every call rather than captured, so re-injecting into a document that shares
101
+ * a `Window` with an earlier one (an in-place `document.write` rebuild) changes
102
+ * the answer instead of piling a second patch on the first.
103
+ */
104
+ export declare const PRINT_PREVIEW_GLOBAL = "__ptxPrintPreview";
105
+ /**
106
+ * The inline `<script>` (as a string) that makes pretext-core.js see a
107
+ * `printpreview` query parameter, or — with no id — stop seeing one.
108
+ *
109
+ * Written in ES5-ish, self-contained JS because it is embedded verbatim in the
110
+ * output page (no bundler runs over it) and re-executes on every in-place
111
+ * `document.write` rebuild.
112
+ *
113
+ * The wrapper is deliberately narrow: it adds the parameter only when the
114
+ * caller passed the location's own search string, which is what pretext-core.js
115
+ * does and what nothing else has reason to do. Everything else — a
116
+ * `URLSearchParams` built from a form, from another URL, from an object — is
117
+ * constructed and returned untouched. Returning a real `URLSearchParams` from
118
+ * the constructor (rather than subclassing) keeps `instanceof` honest and works
119
+ * whether or not the caller says `new`.
120
+ */
121
+ export declare function printPreviewBridgeScript(printoutId?: string): string;
122
+ /**
123
+ * Deliver a rendered page in the print-preview layout for one of its printouts,
124
+ * or — with no id — in the ordinary layout.
125
+ *
126
+ * Inject into `<head>`, ahead of pretext-core.js: the patch has to be in place
127
+ * before that script's DOMContentLoaded handler *runs*, and being earlier in
128
+ * the document is the simplest way to be sure of it.
129
+ *
130
+ * An id that is not a printout on this page is treated as no id at all. That is
131
+ * not merely defensive: pretext-core.js swaps in the print stylesheet *before*
132
+ * it looks the element up, so a stale id — one left over from the previously
133
+ * previewed file, say — would leave the reader on a print-styled page with no
134
+ * printout on it.
135
+ *
136
+ * Returns the page with the bridge removed (never with a half-applied one) when
137
+ * there is no `<head>` to inject into, which would mean the page is not a
138
+ * PreTeXt render at all.
139
+ */
140
+ export declare function injectPrintPreview(html: string, printoutId?: string): string;
@@ -0,0 +1,122 @@
1
+ const p = /<a\b[^>]*\bdata-printout="[^"]*"[^>]*>/gi;
2
+ function s(t, e) {
3
+ const n = new RegExp(`\\b${e}="([^"]*)"`, "i").exec(t);
4
+ return n ? u(n[1]) : void 0;
5
+ }
6
+ function u(t) {
7
+ return t.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;|&#39;/g, "'").replace(/&#x27;/gi, "'").replace(/&amp;/g, "&");
8
+ }
9
+ function l(t, e, n) {
10
+ const i = [t, e].filter(Boolean).join(" ");
11
+ return i && n ? `${i}: ${n}` : i || n;
12
+ }
13
+ function w(t) {
14
+ const e = [], n = /* @__PURE__ */ new Set();
15
+ for (const [i] of t.matchAll(p)) {
16
+ const r = s(i, "data-printout");
17
+ if (!r || n.has(r))
18
+ continue;
19
+ n.add(r);
20
+ const o = s(i, "data-printout-type") ?? "", c = s(i, "data-printout-number") ?? "", a = s(i, "data-printout-title") ?? "";
21
+ e.push({
22
+ id: r,
23
+ type: o,
24
+ number: c,
25
+ title: a,
26
+ label: l(o, c, a)
27
+ });
28
+ }
29
+ return e;
30
+ }
31
+ const f = /* @__PURE__ */ new Set([
32
+ "worksheet",
33
+ "handout",
34
+ "project",
35
+ "activity",
36
+ "exploration",
37
+ "investigation"
38
+ ]);
39
+ function E(t, e) {
40
+ if (!(!e || !f.has(e)))
41
+ return t[0]?.id;
42
+ }
43
+ const g = "__ptxPrintPreview";
44
+ function v(t) {
45
+ const e = JSON.stringify(t ?? null);
46
+ return [
47
+ "<script>",
48
+ "(function () {",
49
+ " var KEY = " + JSON.stringify(g) + ";",
50
+ " window[KEY] = " + e + ";",
51
+ // Pin the page to light mode for the print layout; see printPreviewCss.
52
+ // `data-darkmode="disabled"` is upstream's own switch — pretext-core.js
53
+ // checks it in both isDarkMode and setDarkMode, the only two places that
54
+ // touch the class — and the theme bridge honours it too (theme.ts), so a
55
+ // later theme message from the embedder cannot undo this. Nothing removes
56
+ // the attribute again: leaving print preview delivers a new document,
57
+ // whose <html> carries whatever the render authored.
58
+ " if (window[KEY] && window.document.documentElement) {",
59
+ " var root = window.document.documentElement;",
60
+ " if (root.classList) { root.classList.remove('dark-mode'); }",
61
+ " root.setAttribute('data-darkmode', 'disabled');",
62
+ " }",
63
+ // The patch survives a document.write rebuild along with the Window, so
64
+ // installing it twice would stack wrappers; the id above is what changes.
65
+ " if (window.__ptxPrintPreviewPatched) { return; }",
66
+ " var Base = window.URLSearchParams;",
67
+ " if (!Base) { return; }",
68
+ " window.__ptxPrintPreviewPatched = true;",
69
+ " function Patched(init) {",
70
+ " var params = new Base(init === undefined || init === null ? '' : init);",
71
+ " var id = window[KEY];",
72
+ " if (id && typeof init === 'string' && init === window.location.search) {",
73
+ " params.set('printpreview', id);",
74
+ " }",
75
+ " return params;",
76
+ " }",
77
+ " Patched.prototype = Base.prototype;",
78
+ " window.URLSearchParams = Patched;",
79
+ "})();",
80
+ "<\/script>"
81
+ ].join(`
82
+ `);
83
+ }
84
+ function P() {
85
+ return [
86
+ "<style>",
87
+ ":root {",
88
+ " color-scheme: light;",
89
+ "}",
90
+ // !important because the rule this has to beat is the *host's*, not the
91
+ // page's: VS Code injects a `body { background-color:
92
+ // var(--vscode-editor-background) }` of its own into every webview
93
+ // document, and where it injects it is not ours to rely on. The values
94
+ // still come from the print stylesheet's variables, so it stays in charge
95
+ // of what "paper" looks like.
96
+ "html, body {",
97
+ " background: var(--page-color, #fff) !important;",
98
+ " color: var(--body-text-color, #000) !important;",
99
+ "}",
100
+ "</style>"
101
+ ].join(`
102
+ `);
103
+ }
104
+ const h = "<!--ptx-print-preview-->", m = "<!--/ptx-print-preview-->", x = /\n?<!--ptx-print-preview-->[\s\S]*?<!--\/ptx-print-preview-->/;
105
+ function b(t, e) {
106
+ const n = x.exec(t), i = n ? t.slice(0, n.index) + t.slice(n.index + n[0].length) : t, r = /<head[^>]*>/i.exec(i);
107
+ if (!r)
108
+ return i;
109
+ const o = e && w(i).some((d) => d.id === e) ? e : void 0, c = `
110
+ ${h}
111
+ ${v(o)}
112
+ ${o ? `${P()}
113
+ ` : ""}${m}`, a = r.index + r[0].length;
114
+ return i.slice(0, a) + c + i.slice(a);
115
+ }
116
+ export {
117
+ g as PRINT_PREVIEW_GLOBAL,
118
+ b as injectPrintPreview,
119
+ w as listPrintouts,
120
+ v as printPreviewBridgeScript,
121
+ E as rootPrintoutId
122
+ };
@@ -1,4 +1,5 @@
1
1
  import { PreviewBannerOptions } from './banner.js';
2
+ import { PrintoutInfo } from './printout.js';
2
3
  import { RevealView } from './reveal.js';
3
4
  import { PtxSourceMap } from './sourcemap.js';
4
5
  import { RenderTarget } from './target.js';
@@ -229,6 +230,21 @@ export interface RenderOptions {
229
230
  * settings"), so there is no default text here.
230
231
  */
231
232
  previewBanner?: PreviewBannerOptions;
233
+ /**
234
+ * Open the page in the print-preview layout for the printout with this HTML
235
+ * id — paginated to a paper size, headers and footers, workspaces at their
236
+ * true height (see printout.ts). The ids to choose from come back as
237
+ * {@link RenderResult.printouts}; {@link RenderResult.rootPrintout} names the
238
+ * one worth defaulting to.
239
+ *
240
+ * Omit for the ordinary layout, which leaves the output byte-identical to a
241
+ * render without this option. An embedder that toggles the layout should
242
+ * re-inject over the rendered HTML instead of re-rendering
243
+ * (`injectPrintPreview`), which is both instant and — because it states the
244
+ * *off* case explicitly — the only way to leave print preview in a host that
245
+ * rewrites its document in place.
246
+ */
247
+ printPreview?: string;
232
248
  }
233
249
  export interface RenderResult {
234
250
  /** Complete standalone HTML page (CDN-hosted css/js/MathJax). */
@@ -241,6 +257,20 @@ export interface RenderResult {
241
257
  target: RenderTarget;
242
258
  /** Present when RenderOptions.sourceMap was set. */
243
259
  sourceMap?: PtxSourceMap;
260
+ /**
261
+ * The page's printouts — worksheets, handouts, and projects with a workspace
262
+ * — in document order, each of which can be shown in the print-preview
263
+ * layout (see printout.ts). Empty when the document has none, which is also
264
+ * when the page carries no print controls to drive that layout with.
265
+ */
266
+ printouts: PrintoutInfo[];
267
+ /**
268
+ * The printout to open in print preview by default, set when the rendered
269
+ * document *is* a printout rather than merely containing some — previewing a
270
+ * `worksheet.ptx`, whose root element is the `<worksheet>` itself. Undefined
271
+ * otherwise, meaning the page should open in the ordinary layout.
272
+ */
273
+ rootPrintout?: string;
244
274
  /**
245
275
  * Absolute directories that the `external/` and `generated/` URL prefixes in
246
276
  * `html` refer to, resolved from the publication file against the main
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretextbook/pretext-html",
3
- "version": "0.10.0",
3
+ "version": "0.11.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",
@@ -26,6 +26,9 @@
26
26
  ],
27
27
  "reveal": [
28
28
  "./dist/reveal.d.ts"
29
+ ],
30
+ "printout": [
31
+ "./dist/printout.d.ts"
29
32
  ]
30
33
  }
31
34
  },
@@ -57,6 +60,11 @@
57
60
  "import": "./dist/reveal.js",
58
61
  "default": "./dist/reveal.js"
59
62
  },
63
+ "./printout": {
64
+ "types": "./dist/printout.d.ts",
65
+ "import": "./dist/printout.js",
66
+ "default": "./dist/printout.js"
67
+ },
60
68
  "./cli": "./dist/cli.js"
61
69
  },
62
70
  "sideEffects": false,