@uniweb/press 0.4.16 → 0.4.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/press",
3
- "version": "0.4.16",
3
+ "version": "0.4.18",
4
4
  "description": "Press your React components into downloadable files — Word, Excel, PDF. Registration-based pattern with lazy-loaded format adapters.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,89 +1,425 @@
1
1
  /**
2
2
  * @fileoverview The CSS a Temml-produced MathML document needs to lay out.
3
3
  *
4
- * Math arrives as MathML, which browsers render natively — so it is tempting
5
- * to conclude, as this pipeline's own comments once did, that it carries no
6
- * CSS dependency at all. It does, for two separate reasons:
4
+ * Math arrives as MathML, which browsers render natively — so it is tempting to
5
+ * conclude, as this pipeline's own comments once did, that it carries no CSS
6
+ * dependency at all. It carries a large one, and `<menclose>` is the proof:
7
+ * that element is NOT part of MathML-Core, it is Temml's own polyfill, and the
8
+ * rules that draw `\cancel`, `\overline` and `\sout` over it exist only in
9
+ * Temml's stylesheet. Without them those constructs render as the bare term — a
10
+ * formula that means something different from what the author wrote.
7
11
  *
8
- * 1. **Temml emits class hooks and expects a stylesheet to define them.**
9
- * `tml-left` / `tml-right` carry the `&` column alignment of an `aligned`
10
- * environment, and `tml-jot` carries its extra row spacing. With nothing
11
- * defining them, a derivation's `=` signs do not line up and its rows sit
12
- * flush against each other. The declarations below are Temml's own, from
13
- * `Temml-Local.css`.
12
+ * ── Why this is a vendored string and not an import ──
14
13
  *
15
- * 2. **`mtd` gets no vertical padding from the browser.** Temml's stylesheet
16
- * only *adjusts* row spacing for jot and small, on the stated assumption
17
- * that "default mtd top padding is 0.5ex per MathML-Core and user-agent
18
- * CSS". Measured in Chrome 2026-07, a pristine `mtd` gets 0px — so a
19
- * `pmatrix` or `cases`, which carries no `tml-*` class at all, renders with
20
- * its rows touching. Shipping Temml's stylesheet verbatim fixes the
21
- * derivation and leaves the matrix broken; the `math mtd` rule below is the
22
- * part Temml does not have.
14
+ * The browser lane (`@uniweb/kit/math-tokens.css`) simply does
15
+ * `@import "temml/dist/Temml-Local.css"` and lets the bundler resolve it. press
16
+ * cannot: its adapters EMIT CSS as a string into a document, run in browser
17
+ * hosts as well as Node ones (see the host-supplied asset loaders in
18
+ * `assets/fetch.js`), and press has — by constitution — no `@uniweb/*`
19
+ * dependency at all. So the declarations are genuinely duplicated here.
23
20
  *
24
- * ── Why this is a string and not an imported stylesheet ──
21
+ * That duplicate is pinned mechanically rather than by comment:
22
+ * `framework/_contracts/math-css-parity.test.js` recomposes this value from the
23
+ * real `temml` package plus kit's stylesheet and fails when they disagree.
24
+ * Regenerate rather than hand-edit `TEMML_BASE`.
25
25
  *
26
- * These adapters run in browser hosts as well as Node ones (see the host-
27
- * supplied asset loaders in `assets/fetch.js`), so press cannot read a file at
28
- * runtime, and it does not depend on `@uniweb/kit`. The same declarations
29
- * therefore live in `kit/src/prose-tokens.css` for the browser lane.
26
+ * ── The two deliberate departures from Temml's sheet ──
30
27
  *
31
- * That is a duplicate, and duplicates drift — so it is pinned mechanically
32
- * rather than by comment: `framework/_contracts/math-css-parity.test.js`
33
- * parses both and fails when they disagree. Change one, change the other, or
34
- * the contract test names it.
28
+ * 1. **`@font-face` is stripped.** It points at `Temml.woff2`, which a bundler
29
+ * emits as an asset for the browser lane but which press would have to
30
+ * package into the EPUB itself. Fixing it is an asset question for the EPUB
31
+ * adapter, not a CSS one.
32
+ *
33
+ * Consequence, accepted for now, and it is wider than it first looks: two
34
+ * rules want that face — `math .mathscr` and, more commonly, `mo.tml-prime`,
35
+ * so every `f'` and `f''` in a compiled document loses its prime alignment
36
+ * and falls back to a system glyph. (It is NOT what draws `\mathcal`; that
37
+ * is `*.mathcal { font-feature-settings: 'ss01' }` and survives stripping.)
38
+ *
39
+ * 2. **`.tml-eqn::before` is rescoped to `.tml-eqn:empty::before`.** Temml
40
+ * leaves the tag span empty and asks a CSS counter to draw the number. That
41
+ * works in a browser and not in a document: Paged.js rewrites counters for
42
+ * its own pagination and strips `counter-increment`, so every equation
43
+ * rendered as "(0)" (measured 2026-07-31). `numberEquations` below therefore
44
+ * writes the number in as TEXT — and a span carrying a number is no longer
45
+ * `:empty`, so the counter rule and the text can never both fire.
46
+ */
47
+
48
+ /**
49
+ * Temml's own stylesheet, vendored. Generated from `temml/dist/Temml-Local.css`
50
+ * with the two departures described above. Do not hand-edit.
35
51
  */
52
+ const TEMML_BASE = `math {
53
+ font-family: "Cambria Math", 'STIXTwoMath-Regular', 'NotoSansMath-Regular', math;
54
+ font-style: normal;
55
+ font-weight: normal;
56
+ line-height: normal;
57
+ font-size-adjust: none;
58
+ text-indent: 0;
59
+ text-transform: none;
60
+ letter-spacing: normal;
61
+ word-wrap: normal;
62
+ direction: ltr;
63
+ /* Prevent Firefox from omitting the dot on i or j. */
64
+ font-feature-settings: "dtls" off;
65
+ }
66
+
67
+ math * {
68
+ border-color: currentColor;
69
+ }
70
+
71
+ /* display: block is necessary in Firefox and Safari.
72
+ * Not in Chromium, which recognizes display: "block math" written inline. */
73
+ math.tml-display {
74
+ display: block;
75
+ width: 100%;
76
+ }
77
+
78
+ *.mathcal {
79
+ /* NotoSans */
80
+ font-feature-settings: 'ss01';
81
+ }
82
+
83
+ math .mathscr {
84
+ font-family: "Temml";
85
+ }
86
+
87
+ mo.tml-prime {
88
+ font-family: Temml;
89
+ }
90
+
91
+ /* Cramped superscripts in WebKit */
92
+ mfrac > :nth-child(2),
93
+ msqrt,
94
+ mover > :first-child {
95
+ math-shift: compact
96
+ }
97
+
98
+ .menclose {
99
+ display: inline-block;
100
+ position: relative;
101
+ padding: 0.5ex 0ex;
102
+ }
103
+ .tml-cancelto {
104
+ display: inline-block;
105
+ position: absolute;
106
+ top: 0;
107
+ left: 0;
108
+ padding: 0.5ex 0ex;
109
+ background-color: currentColor;
110
+ /* Use the SVG as an alpha mask (painted by background-color) */
111
+ -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");
112
+ mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");
113
+ -webkit-mask-repeat: no-repeat;
114
+ mask-repeat: no-repeat;
115
+ -webkit-mask-size: 100% 100%;
116
+ mask-size: 100% 100%;
117
+ -webkit-mask-position: 0 0;
118
+ mask-position: 0 0;
119
+ }
120
+
121
+ @supports (-moz-appearance: none) {
122
+ /* \vec w/o italic correction for Firefox */
123
+ .tml-vec {
124
+ transform: scale(0.75)
125
+ }
126
+ /* Fix \cancelto in Firefox */
127
+ .ff-narrow {
128
+ width: 0em;
129
+ }
130
+ .ff-nudge-left {
131
+ margin-left: -0.2em;
132
+ }
133
+ }
36
134
 
37
- export const MATH_CSS = `/* Temml's own class hooks — without these an aligned environment neither
38
- aligns nor breathes. */
39
- .tml-right { text-align: right; }
40
- .tml-left { text-align: left; }
41
- .tml-sml-pad { padding-left: 0.05em; }
42
- /* Row spacing. The browser gives mtd no vertical padding, so state the
43
- MathML-Core default here; Temml's own CSS only adjusts it for jot. */
44
- math mtd { padding-top: 0.5ex; padding-bottom: 0.5ex; }
45
- math mtable.tml-jot mtd { padding-top: 0.7ex; padding-bottom: 0.7ex; }
46
- /* AMS auto-numbering, for lanes that keep our CSS counter.
47
-
48
- Scoped to :empty because the document lanes cannot rely on it -- Paged.js
49
- rewrites counters for its own pagination and strips counter-increment, so
50
- every equation rendered as "(0)" (measured 2026-07-31; the declaration
51
- survives intact without the polyfill). press therefore writes the numbers
52
- into the spans as text, and a span carrying a number is no longer :empty, so
53
- the two can never both fire.
54
-
55
- AMS auto-numbering. Which equations number is the AUTHOR's choice, made in
56
- LaTeX: align and equation number, aligned and the starred forms do not.
57
- Without these two rules that choice was discarded -- align and align-star
58
- rendered identically, so an author who asked for numbers silently got none.
59
- (No backticks in here: this string is a JS template literal.) */
135
+ @supports (not (-moz-appearance: none)) {
136
+ /* Chromium and WebKit */
137
+ /* prime vertical alignment */
138
+ mo.tml-prime {
139
+ font-family: Temml;
140
+ }
141
+ /* Italic correction on superscripts */
142
+ .tml-sml-pad {
143
+ padding-left: 0.05em;
144
+ }
145
+ .tml-med-pad {
146
+ padding-left: 0.10em;
147
+ }
148
+ .tml-lrg-pad {
149
+ padding-left: 0.15em;
150
+ }
151
+ }
152
+
153
+ @supports (-webkit-backdrop-filter: blur(1px)) {
154
+ /* WebKit vertical & italic correction on accents */
155
+ .wbk-acc {
156
+ /* lower by x-height distance */
157
+ transform: translate(0em, 0.431em);
158
+ }
159
+ .wbk-sml {
160
+ transform: translate(0.07em, 0);
161
+ }
162
+ .wbk-sml-acc {
163
+ transform: translate(0.07em, 0.431em);
164
+ }
165
+ .wbk-sml-vec {
166
+ transform: scale(0.75) translate(0.07em, 0);
167
+ }
168
+ .wbk-med {
169
+ transform: translate(0.14em, 0);
170
+ }
171
+ .wbk-med-acc {
172
+ transform: translate(0.14em, 0.431em);
173
+ }
174
+ .wbk-med-vec {
175
+ transform: scale(0.75) translate(0.14em, 0);
176
+ }
177
+ .wbk-lrg {
178
+ transform: translate(0.21em, 0);
179
+ }
180
+ .wbk-lrg-acc {
181
+ transform: translate(0.21em, 0.431em);
182
+ }
183
+ .wbk-lrg-vec {
184
+ transform: scale(0.75) translate(0.21em, 0);
185
+ }
186
+ }
187
+
188
+ /* \cancel & \phase use background images. Get them to print. */
189
+ menclose {
190
+ -webkit-print-color-adjust: exact; /* Chrome & Edge */
191
+ print-color-adjust: exact;
192
+ }
193
+
194
+ /* Array cell justification in Firefox & WebKit */
195
+ .tml-right {
196
+ text-align: right;
197
+ }
198
+ .tml-left {
199
+ text-align: left;
200
+ }
201
+
202
+ /* For CD labels that grow to the left in Firefox and WebKit */
203
+ .tml-shift-left { margin-left:-200% }
204
+
205
+ /* Styles for Chromium only */
206
+ @supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) {
207
+ /* Italic correction on accents */
208
+ .chr-sml {
209
+ transform: translate(0.07em, 0)
210
+ }
211
+ .chr-sml-vec {
212
+ transform: scale(0.75) translate(0.07em, 0)
213
+ }
214
+ .chr-med {
215
+ transform: translate(0.14em, 0)
216
+ }
217
+ .chr-med-vec {
218
+ transform: scale(0.75) translate(0.14em, 0)
219
+ }
220
+ .chr-lrg {
221
+ transform: translate(0.21em, 0)
222
+ }
223
+ .chr-lrg-vec {
224
+ transform: scale(0.75) translate(0.21em, 0)
225
+ }
226
+
227
+ /* For CD labels that grow to the left */
228
+ .tml-shift-left { margin-left:-100% }
229
+
230
+ /* MathML Core & Chromium do not support the MathML 3.0 element <menclose> attributes. */
231
+ /* So use styles. */
232
+ menclose {
233
+ position: relative;
234
+ padding: 0.5ex 0ex;
235
+ }
236
+
237
+ .tml-overline {
238
+ padding: 0.1em 0 0 0;
239
+ border-top: 0.065em solid;
240
+ }
241
+
242
+ .tml-underline {
243
+ padding: 0 0 0.1em 0;
244
+ border-bottom: 0.065em solid;
245
+ }
246
+
247
+ .tml-cancel {
248
+ display: inline-block;
249
+ position: absolute;
250
+ left: 0.5px;
251
+ bottom: 0;
252
+ width: 100%;
253
+ height: 100%;
254
+ background-color: currentColor;
255
+ }
256
+ .upstrike {
257
+ clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em);
258
+ }
259
+ .downstrike {
260
+ clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%);
261
+ }
262
+ .sout {
263
+ clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em));
264
+ }
265
+ .tml-xcancel {
266
+ clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em));
267
+ }
268
+
269
+ .longdiv-top {
270
+ border-top: 0.067em solid;
271
+ padding: 0.1em 0.2em 0.2em 0.433em;
272
+ }
273
+ .longdiv-arc {
274
+ position: absolute;
275
+ top: 0;
276
+ bottom: 0.1em;
277
+ left: -0.4em;
278
+ width: 0.7em;
279
+ border: 0.067em solid;
280
+ transform: translateY(-0.067em);
281
+ border-radius: 70%;
282
+ clip-path: inset(0 0 0 0.4em);
283
+ box-sizing: border-box;}
284
+ .menclose {display: inline-block;
285
+ text-align: left;
286
+ position: relative;
287
+ }
288
+
289
+ .phasor-bottom {
290
+ border-bottom: 0.067em solid;
291
+ padding: 0.2em 0.2em 0.1em 0.6em;
292
+ }
293
+ .phasor-angle {
294
+ display: inline-block;
295
+ position: absolute;
296
+ left: 0.5px;
297
+ bottom: -0.04em;
298
+ height: 100%;
299
+ aspect-ratio: 0.5;
300
+ background-color: currentColor;
301
+ clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em);
302
+ }
303
+
304
+ .tml-fbox {
305
+ padding: 3pt;
306
+ border: 1px solid;
307
+ }
308
+
309
+ .circle-pad {
310
+ padding: 0.267em;
311
+ }
312
+ .textcircle {
313
+ position: absolute;
314
+ top: 0;
315
+ bottom: 0;
316
+ right: 0;
317
+ left: 0;
318
+ border: 0.067em solid;
319
+ border-radius: 50%;
320
+ }
321
+
322
+ .actuarial {
323
+ padding: 0.03889em 0.03889em 0 0.03889em;
324
+ border-width: 0.08em 0.08em 0em 0em;
325
+ border-style: solid;
326
+ margin-right: 0.03889em;
327
+ }
328
+
329
+ /* Stretch \widetilde */
330
+ .tml-crooked-2 {
331
+ transform: scale(2.0, 1.1)
332
+ }
333
+ .tml-crooked-3 {
334
+ transform: scale(3.0, 1.3)
335
+ }
336
+ .tml-crooked-4 {
337
+ transform: scale(4.0, 1.4)
338
+ }
339
+ /* set array cell justification */
340
+ .tml-right {
341
+ text-align: -webkit-right;
342
+ }
343
+ .tml-left {
344
+ text-align: -webkit-left;
345
+ }
346
+ }
347
+
348
+ .special-fraction {
349
+ font-family: 'STIX TWO', 'Times New Roman', Times, Tinos, serif;
350
+ }
351
+
352
+ /* flex-wrap for line-breaking in Chromium */
353
+ math {
354
+ display: inline-flex;
355
+ flex-wrap: wrap;
356
+ align-items: baseline;
357
+ }
358
+ math > mrow {
359
+ padding: 0.5ex 0ex;
360
+ }
361
+
362
+ /* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */
363
+ /* We adjust for jot and small */
364
+ mtable.tml-jot mtd {
365
+ padding-top: 0.7ex;
366
+ padding-bottom: 0.7ex;
367
+ }
368
+ mtable.tml-small mtd {
369
+ padding-top: 0.35ex;
370
+ padding-bottom: 0.35ex;
371
+ }
372
+
373
+ /* Firefox */
374
+ @-moz-document url-prefix() {
375
+ /* Avoid flex-wrap */
376
+ math { display: inline; }
377
+ math > mrow { padding: 0 }
378
+ /* Adjust Firefox spacing between array rows */
379
+ mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; }
380
+ mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; }
381
+ }
382
+
383
+ /* AMS environment auto-numbering via CSS counter. */
60
384
  .tml-eqn:empty::before {
61
385
  counter-increment: tmlEqnNo;
62
386
  content: "(" counter(tmlEqnNo) ")";
63
387
  }
388
+
64
389
  body {
65
390
  counter-reset: tmlEqnNo;
391
+ }`
392
+
393
+ /**
394
+ * What Temml does not have. Kept byte-identical to the rules in
395
+ * `@uniweb/kit/math-tokens.css`, which the parity test enforces.
396
+ *
397
+ * 1. `mtd` padding: Temml's sheet only ADJUSTS row spacing for jot and small,
398
+ * on the stated assumption that "default mtd top padding is 0.5ex per
399
+ * MathML-Core and user-agent CSS". Measured in Chrome (2026-07), a pristine
400
+ * `mtd` gets 0px, and a `pmatrix` or `cases` carries no `tml-*` class at
401
+ * all — so its rows touch. Guarded to exclude Firefox because Temml
402
+ * deliberately ZEROES mtd padding there, and an unguarded `math mtd`
403
+ * outranks it on specificity (0,0,2 vs 0,0,1) regardless of the at-rule.
404
+ *
405
+ * 2. Display math needs CSS block layout, not MathML layout, for an equation
406
+ * TAG to reach the right margin. Temml already declares
407
+ * `math.tml-display { display: block; width: 100% }` and cannot win: it emits
408
+ * `style="display:block math"` inline on every display formula, and an
409
+ * inline style beats any stylesheet rule. This restores Temml's own intent
410
+ * with the `!important` its rule lacks, scoped with `:has()` so unnumbered
411
+ * formulas keep MathML layout and stay centred.
412
+ */
413
+ const UNIWEB_CORRECTIONS = `@supports (not (-moz-appearance: none)) {
414
+ math mtd { padding-top: 0.5ex; padding-bottom: 0.5ex; }
66
415
  }
67
- /* Display math needs CSS block layout, not MathML layout, for an equation TAG
68
- to reach the right margin: the tag rides in an mtable whose width:100%
69
- Chromium ignores under display: block math, collapsing the spacer cells so
70
- "(1)" sits glued to the equation instead of at the margin.
71
-
72
- !important is not decoration. Temml emits style=display:block math on
73
- every display formula unconditionally — no option turns it off — so an inline
74
- style beats any rule we write, including Temml's own math.tml-display { display: block }, which is exactly what this restores. Fixing it here
75
- rather than in the generator also repairs math that was already built.
76
-
77
- Scoped with :has() to formulas that actually carry a tag. Switching every
78
- display formula to CSS block layout would left-align the lot -- MathML layout
79
- is what centres them -- so an unnumbered derivation or matrix keeps
80
- display: block math and stays centred. Where :has() is unsupported the tag
81
- simply does not reach the margin; nothing else changes. */
82
416
  math.tml-display:has(.tml-eqn) {
83
417
  display: block !important;
84
418
  width: 100%;
85
419
  }`
86
420
 
421
+ export const MATH_CSS = `${TEMML_BASE}\n${UNIWEB_CORRECTIONS}`
422
+
87
423
  /**
88
424
  * Write equation numbers into Temml's tag spans as text.
89
425
  *
@@ -107,6 +107,23 @@ function renderElement(element, key) {
107
107
  />
108
108
  )
109
109
 
110
+ case 'document':
111
+ // A file, not a picture. In print it can only be shown, not opened:
112
+ // its preview when it has one, captioned with its name — and
113
+ // nothing when it has none, as a video is skipped. Until 2026-09-29
114
+ // the parser delivered a document as an image, and this printed
115
+ // the file itself as one.
116
+ if (!element.attrs?.preview) return null
117
+ return (
118
+ <Image
119
+ key={key}
120
+ src={element.attrs.preview}
121
+ alt=""
122
+ caption={element.attrs.caption || element.attrs.alt}
123
+ id={element.attrs.id || undefined}
124
+ />
125
+ )
126
+
110
127
  // Skipped: dataBlock (not prose), video (not in print), inset (Phase 3).
111
128
  default:
112
129
  return null