@ai-matrx/print 0.8.7 → 0.8.9
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/CHANGELOG.md +51 -0
- package/dist/document.cjs +256 -8
- package/dist/document.cjs.map +1 -1
- package/dist/document.d.cts +10 -2
- package/dist/document.d.ts +10 -2
- package/dist/document.js +256 -8
- package/dist/document.js.map +1 -1
- package/dist/markdown-html.cjs +4207 -0
- package/dist/markdown-html.cjs.map +1 -0
- package/dist/markdown-html.d.cts +87 -0
- package/dist/markdown-html.d.ts +87 -0
- package/dist/markdown-html.js +4176 -0
- package/dist/markdown-html.js.map +1 -0
- package/dist/markdown.cjs +146 -36
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +146 -36
- package/dist/markdown.js.map +1 -1
- package/dist/safe-html.cjs +299 -0
- package/dist/safe-html.cjs.map +1 -0
- package/dist/safe-html.d.cts +56 -0
- package/dist/safe-html.d.ts +56 -0
- package/dist/safe-html.js +278 -0
- package/dist/safe-html.js.map +1 -0
- package/package.json +27 -3
package/dist/document.d.cts
CHANGED
|
@@ -548,14 +548,22 @@ interface HtmlRenderContext {
|
|
|
548
548
|
xhtml?: boolean;
|
|
549
549
|
/** Where an in-document id lives (EPUB chapters split the document). */
|
|
550
550
|
hrefFor?: (id: string) => string;
|
|
551
|
-
/** Image URL override (EPUB packages images inside the book). */
|
|
552
|
-
srcFor?: (url: string) => string;
|
|
551
|
+
/** Image URL override (EPUB packages images inside the book; the Markdown→HTML engine's asset port). `null` = unavailable. */
|
|
552
|
+
srcFor?: (url: string) => string | null;
|
|
553
553
|
}
|
|
554
554
|
declare function renderBlocksHtml(blocks: Block[], x: HtmlRenderContext): string;
|
|
555
555
|
/** The print stylesheet for a document's settings. Light mode, always. */
|
|
556
556
|
declare function documentCss(s: DocumentSettings): string;
|
|
557
557
|
interface DocumentHtmlOptions {
|
|
558
558
|
images?: ImageMap | undefined;
|
|
559
|
+
/** Image URL override (see `HtmlRenderContext.srcFor`). */
|
|
560
|
+
srcFor?: ((url: string) => string | null) | undefined;
|
|
561
|
+
/**
|
|
562
|
+
* Rewrites the rendered body (cover + sections) before it is placed in the
|
|
563
|
+
* page — the Markdown→HTML engine renders kind atoms and applies the
|
|
564
|
+
* allow-list here, so the trusted page shell and its CSS stay intact.
|
|
565
|
+
*/
|
|
566
|
+
transformBody?: ((html: string) => string) | undefined;
|
|
559
567
|
}
|
|
560
568
|
/** A complete, standalone print-grade HTML document. */
|
|
561
569
|
declare function documentToHtml(tree: DocTree, options?: DocumentHtmlOptions): string;
|
package/dist/document.d.ts
CHANGED
|
@@ -548,14 +548,22 @@ interface HtmlRenderContext {
|
|
|
548
548
|
xhtml?: boolean;
|
|
549
549
|
/** Where an in-document id lives (EPUB chapters split the document). */
|
|
550
550
|
hrefFor?: (id: string) => string;
|
|
551
|
-
/** Image URL override (EPUB packages images inside the book). */
|
|
552
|
-
srcFor?: (url: string) => string;
|
|
551
|
+
/** Image URL override (EPUB packages images inside the book; the Markdown→HTML engine's asset port). `null` = unavailable. */
|
|
552
|
+
srcFor?: (url: string) => string | null;
|
|
553
553
|
}
|
|
554
554
|
declare function renderBlocksHtml(blocks: Block[], x: HtmlRenderContext): string;
|
|
555
555
|
/** The print stylesheet for a document's settings. Light mode, always. */
|
|
556
556
|
declare function documentCss(s: DocumentSettings): string;
|
|
557
557
|
interface DocumentHtmlOptions {
|
|
558
558
|
images?: ImageMap | undefined;
|
|
559
|
+
/** Image URL override (see `HtmlRenderContext.srcFor`). */
|
|
560
|
+
srcFor?: ((url: string) => string | null) | undefined;
|
|
561
|
+
/**
|
|
562
|
+
* Rewrites the rendered body (cover + sections) before it is placed in the
|
|
563
|
+
* page — the Markdown→HTML engine renders kind atoms and applies the
|
|
564
|
+
* allow-list here, so the trusted page shell and its CSS stay intact.
|
|
565
|
+
*/
|
|
566
|
+
transformBody?: ((html: string) => string) | undefined;
|
|
559
567
|
}
|
|
560
568
|
/** A complete, standalone print-grade HTML document. */
|
|
561
569
|
declare function documentToHtml(tree: DocTree, options?: DocumentHtmlOptions): string;
|
package/dist/document.js
CHANGED
|
@@ -87,6 +87,249 @@ function contentColumnWidths(b, n, avail, measure, pad = 10) {
|
|
|
87
87
|
|
|
88
88
|
// src/document/math.ts
|
|
89
89
|
import { listIslands, tokenizeSource } from "@ai-matrx/content-ir/source";
|
|
90
|
+
|
|
91
|
+
// src/safe-html.ts
|
|
92
|
+
import { fromHtml } from "hast-util-from-html";
|
|
93
|
+
import { defaultSchema, sanitize } from "hast-util-sanitize";
|
|
94
|
+
import { toHtml } from "hast-util-to-html";
|
|
95
|
+
var STRIP_WITH_CONTENT = [
|
|
96
|
+
"script",
|
|
97
|
+
"style",
|
|
98
|
+
"noscript",
|
|
99
|
+
"template",
|
|
100
|
+
"iframe",
|
|
101
|
+
"object",
|
|
102
|
+
"embed",
|
|
103
|
+
"textarea",
|
|
104
|
+
"select",
|
|
105
|
+
"head",
|
|
106
|
+
"title"
|
|
107
|
+
];
|
|
108
|
+
var FORBIDDEN_TAGS = [
|
|
109
|
+
...STRIP_WITH_CONTENT,
|
|
110
|
+
"form",
|
|
111
|
+
"meta",
|
|
112
|
+
"base",
|
|
113
|
+
"link",
|
|
114
|
+
"frame",
|
|
115
|
+
"frameset",
|
|
116
|
+
"applet",
|
|
117
|
+
"button",
|
|
118
|
+
"svg",
|
|
119
|
+
"math"
|
|
120
|
+
];
|
|
121
|
+
var baseAttributes = defaultSchema.attributes ?? {};
|
|
122
|
+
var SAFE_HTML_SCHEMA = {
|
|
123
|
+
...defaultSchema,
|
|
124
|
+
tagNames: (defaultSchema.tagNames ?? []).filter((t) => !FORBIDDEN_TAGS.includes(t)),
|
|
125
|
+
strip: STRIP_WITH_CONTENT,
|
|
126
|
+
// Authored anchors keep their names; the GitHub `user-content-` rewrite
|
|
127
|
+
// would also break `<label for>` → `<input id>` pairs.
|
|
128
|
+
clobberPrefix: "",
|
|
129
|
+
clobber: []
|
|
130
|
+
};
|
|
131
|
+
var PRINT_CLASS = /^(?:(?:matrx|print|code|doc|language)-[\w-]*|inline-code|is-checked)$/;
|
|
132
|
+
function withPrintClasses(defs = []) {
|
|
133
|
+
let found = false;
|
|
134
|
+
const out = defs.map((d) => {
|
|
135
|
+
if (Array.isArray(d) && d[0] === "className") {
|
|
136
|
+
found = true;
|
|
137
|
+
return [...d, PRINT_CLASS];
|
|
138
|
+
}
|
|
139
|
+
return d;
|
|
140
|
+
});
|
|
141
|
+
return found ? out : [...out, ["className", PRINT_CLASS]];
|
|
142
|
+
}
|
|
143
|
+
var printAttributes = Object.fromEntries(
|
|
144
|
+
Object.entries(baseAttributes).map(([tag, defs]) => [tag, withPrintClasses(defs)])
|
|
145
|
+
);
|
|
146
|
+
var cellAlign = ["style", /^text-align:\s*(?:left|center|right);?$/];
|
|
147
|
+
var PRINT_HTML_SCHEMA = {
|
|
148
|
+
...SAFE_HTML_SCHEMA,
|
|
149
|
+
tagNames: [
|
|
150
|
+
.../* @__PURE__ */ new Set([...SAFE_HTML_SCHEMA.tagNames ?? [], "section", "figure", "figcaption", "label"])
|
|
151
|
+
],
|
|
152
|
+
attributes: {
|
|
153
|
+
...printAttributes,
|
|
154
|
+
"*": [
|
|
155
|
+
...baseAttributes["*"] ?? [],
|
|
156
|
+
["className", PRINT_CLASS],
|
|
157
|
+
"role",
|
|
158
|
+
"ariaLabel",
|
|
159
|
+
"ariaHidden",
|
|
160
|
+
"dataLabel",
|
|
161
|
+
"dataLanguage",
|
|
162
|
+
"dataArtifactType"
|
|
163
|
+
],
|
|
164
|
+
// Table-cell alignment is the one inline style the converter writes.
|
|
165
|
+
td: withPrintClasses([...baseAttributes["td"] ?? [], [...cellAlign]]),
|
|
166
|
+
th: withPrintClasses([...baseAttributes["th"] ?? [], [...cellAlign]]),
|
|
167
|
+
// The flashcard flip is a checkbox + label pair (GitHub forces `disabled`).
|
|
168
|
+
input: withPrintClasses([["type", "checkbox"], "checked"]),
|
|
169
|
+
label: withPrintClasses(["htmlFor"])
|
|
170
|
+
},
|
|
171
|
+
required: { input: { type: "checkbox" } },
|
|
172
|
+
protocols: {
|
|
173
|
+
...defaultSchema.protocols,
|
|
174
|
+
href: [...defaultSchema.protocols?.href ?? [], "tel"],
|
|
175
|
+
// Embedded images (`data:image/png;base64,…`) — an <img> never runs script.
|
|
176
|
+
src: [...defaultSchema.protocols?.src ?? [], "data"]
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
function sanitizeHtmlFragment(html, schema = SAFE_HTML_SCHEMA) {
|
|
180
|
+
if (!html) return "";
|
|
181
|
+
const tree = sanitize(fromHtml(html, { fragment: true }), schema);
|
|
182
|
+
return toHtml(tree, { characterReferences: { useNamedReferences: true } });
|
|
183
|
+
}
|
|
184
|
+
var MATHML_TAGS = [
|
|
185
|
+
"math",
|
|
186
|
+
"maction",
|
|
187
|
+
"annotation",
|
|
188
|
+
"menclose",
|
|
189
|
+
"merror",
|
|
190
|
+
"mfrac",
|
|
191
|
+
"mglyph",
|
|
192
|
+
"mi",
|
|
193
|
+
"mlabeledtr",
|
|
194
|
+
"mmultiscripts",
|
|
195
|
+
"mn",
|
|
196
|
+
"mo",
|
|
197
|
+
"mover",
|
|
198
|
+
"mpadded",
|
|
199
|
+
"mphantom",
|
|
200
|
+
"mprescripts",
|
|
201
|
+
"none",
|
|
202
|
+
"mroot",
|
|
203
|
+
"mrow",
|
|
204
|
+
"ms",
|
|
205
|
+
"mspace",
|
|
206
|
+
"msqrt",
|
|
207
|
+
"mstyle",
|
|
208
|
+
"msub",
|
|
209
|
+
"msubsup",
|
|
210
|
+
"msup",
|
|
211
|
+
"mtable",
|
|
212
|
+
"mtd",
|
|
213
|
+
"mtext",
|
|
214
|
+
"mtr",
|
|
215
|
+
"munder",
|
|
216
|
+
"munderover",
|
|
217
|
+
"semantics"
|
|
218
|
+
];
|
|
219
|
+
var MATHML_ATTRIBUTES = [
|
|
220
|
+
"xmlns",
|
|
221
|
+
"display",
|
|
222
|
+
"displaystyle",
|
|
223
|
+
"mathvariant",
|
|
224
|
+
"mathsize",
|
|
225
|
+
"mathcolor",
|
|
226
|
+
"mathbackground",
|
|
227
|
+
"scriptlevel",
|
|
228
|
+
"dir",
|
|
229
|
+
"fence",
|
|
230
|
+
"form",
|
|
231
|
+
"largeop",
|
|
232
|
+
"lspace",
|
|
233
|
+
"rspace",
|
|
234
|
+
"movablelimits",
|
|
235
|
+
"separator",
|
|
236
|
+
"stretchy",
|
|
237
|
+
"symmetric",
|
|
238
|
+
"maxsize",
|
|
239
|
+
"minsize",
|
|
240
|
+
"accent",
|
|
241
|
+
"accentunder",
|
|
242
|
+
"align",
|
|
243
|
+
"columnalign",
|
|
244
|
+
"rowalign",
|
|
245
|
+
"columnspacing",
|
|
246
|
+
"rowspacing",
|
|
247
|
+
"columnlines",
|
|
248
|
+
"rowlines",
|
|
249
|
+
"frame",
|
|
250
|
+
"framespacing",
|
|
251
|
+
"equalrows",
|
|
252
|
+
"equalcolumns",
|
|
253
|
+
"linethickness",
|
|
254
|
+
"numalign",
|
|
255
|
+
"denomalign",
|
|
256
|
+
"bevelled",
|
|
257
|
+
"notation",
|
|
258
|
+
"width",
|
|
259
|
+
"height",
|
|
260
|
+
"depth",
|
|
261
|
+
"voffset",
|
|
262
|
+
"lquote",
|
|
263
|
+
"rquote",
|
|
264
|
+
"columnspan",
|
|
265
|
+
"rowspan",
|
|
266
|
+
"encoding",
|
|
267
|
+
"open",
|
|
268
|
+
"close",
|
|
269
|
+
"separators",
|
|
270
|
+
"actiontype",
|
|
271
|
+
"selection",
|
|
272
|
+
"mslinebreak",
|
|
273
|
+
"linebreak",
|
|
274
|
+
"indentalign",
|
|
275
|
+
"data*"
|
|
276
|
+
];
|
|
277
|
+
var MATHML_SCHEMA = {
|
|
278
|
+
tagNames: MATHML_TAGS,
|
|
279
|
+
attributes: { "*": MATHML_ATTRIBUTES },
|
|
280
|
+
strip: ["script", "style"],
|
|
281
|
+
clobberPrefix: "",
|
|
282
|
+
clobber: [],
|
|
283
|
+
protocols: {}
|
|
284
|
+
};
|
|
285
|
+
function sanitizeMathml(mathml) {
|
|
286
|
+
return sanitizeHtmlFragment(mathml, MATHML_SCHEMA);
|
|
287
|
+
}
|
|
288
|
+
var MATH_SVG_TAGS = ["svg", "g", "path", "rect", "line", "polyline", "polygon", "circle", "ellipse", "text", "title", "desc"];
|
|
289
|
+
var MATH_SVG_ATTRIBUTES = [
|
|
290
|
+
"xmlns",
|
|
291
|
+
"width",
|
|
292
|
+
"height",
|
|
293
|
+
"viewBox",
|
|
294
|
+
"role",
|
|
295
|
+
"focusable",
|
|
296
|
+
"ariaHidden",
|
|
297
|
+
"ariaLabel",
|
|
298
|
+
"stroke",
|
|
299
|
+
"fill",
|
|
300
|
+
"strokeWidth",
|
|
301
|
+
"strokeLinecap",
|
|
302
|
+
"strokeLinejoin",
|
|
303
|
+
"transform",
|
|
304
|
+
"d",
|
|
305
|
+
"x",
|
|
306
|
+
"y",
|
|
307
|
+
"x1",
|
|
308
|
+
"y1",
|
|
309
|
+
"x2",
|
|
310
|
+
"y2",
|
|
311
|
+
"points",
|
|
312
|
+
"cx",
|
|
313
|
+
"cy",
|
|
314
|
+
"r",
|
|
315
|
+
"rx",
|
|
316
|
+
"ry",
|
|
317
|
+
"dataMmlNode",
|
|
318
|
+
"dataC"
|
|
319
|
+
];
|
|
320
|
+
var MATH_SVG_SCHEMA = {
|
|
321
|
+
tagNames: MATH_SVG_TAGS,
|
|
322
|
+
attributes: { "*": MATH_SVG_ATTRIBUTES },
|
|
323
|
+
strip: ["script", "style", "foreignObject"],
|
|
324
|
+
clobberPrefix: "",
|
|
325
|
+
clobber: [],
|
|
326
|
+
protocols: {}
|
|
327
|
+
};
|
|
328
|
+
function sanitizeMathSvg(svg) {
|
|
329
|
+
return sanitizeHtmlFragment(svg, MATH_SVG_SCHEMA);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/document/math.ts
|
|
90
333
|
var MATH_OPEN = "\uE100";
|
|
91
334
|
var MATH_CLOSE = "\uE101";
|
|
92
335
|
var MATH_TOKEN_RE = /\uE100(\d+)\uE101/g;
|
|
@@ -153,12 +396,12 @@ async function typesetMathSpans(spans) {
|
|
|
153
396
|
const mathml = mj.mml(s.tex, s.display);
|
|
154
397
|
const err = /<merror[\s\S]*?<mtext[^>]*>([\s\S]*?)<\/mtext>/.exec(mathml);
|
|
155
398
|
if (err) {
|
|
156
|
-
out.set(key, { mathml, error: decodeXml(err[1]) });
|
|
399
|
+
out.set(key, { mathml: sanitizeMathml(mathml), error: decodeXml(err[1]) });
|
|
157
400
|
continue;
|
|
158
401
|
}
|
|
159
|
-
const entry = { mathml: mathml.replace(/\n\s*/g, "") };
|
|
402
|
+
const entry = { mathml: sanitizeMathml(mathml.replace(/\n\s*/g, "")) };
|
|
160
403
|
if (s.display) {
|
|
161
|
-
const svg = mj.svg(s.tex, true);
|
|
404
|
+
const svg = sanitizeMathSvg(mj.svg(s.tex, true));
|
|
162
405
|
entry.svg = svg;
|
|
163
406
|
entry.widthEx = Number(/width="([\d.]+)ex"/.exec(svg)?.[1] ?? 0);
|
|
164
407
|
entry.heightEx = Number(/height="([\d.]+)ex"/.exec(svg)?.[1] ?? 0);
|
|
@@ -3268,7 +3511,10 @@ function mathHtml(tex, display, x, inText = false) {
|
|
|
3268
3511
|
}
|
|
3269
3512
|
var href = (id, x) => x.hrefFor ? x.hrefFor(id) : `#${id}`;
|
|
3270
3513
|
function imageSrc(url, x) {
|
|
3271
|
-
if (x.srcFor)
|
|
3514
|
+
if (x.srcFor) {
|
|
3515
|
+
const src = x.srcFor(url);
|
|
3516
|
+
return src === null ? null : safeUrl(src);
|
|
3517
|
+
}
|
|
3272
3518
|
const resolved = x.images?.get(url);
|
|
3273
3519
|
if (resolved) return resolved.dataUrl;
|
|
3274
3520
|
return safeUrl(url);
|
|
@@ -3354,7 +3600,7 @@ function coverHtml(s, x) {
|
|
|
3354
3600
|
s.title ?? ""
|
|
3355
3601
|
)}</h1>${s.subtitle ? `<p class="doc-cover-subtitle">${escapeHtml(s.subtitle)}</p>` : ""}${s.author ? `<p class="doc-cover-author">${escapeHtml(s.author)}</p>` : ""}${s.date ? `<p class="doc-cover-date">${escapeHtml(s.date)}</p>` : ""}</section>`;
|
|
3356
3602
|
}
|
|
3357
|
-
var cssString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, " ")}"`;
|
|
3603
|
+
var cssString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, " ").replace(/</g, "\\3C ").replace(/>/g, "\\3E ")}"`;
|
|
3358
3604
|
function marginContent(template, s) {
|
|
3359
3605
|
if (!template) return "none";
|
|
3360
3606
|
return templateParts(template, s).map((p) => "field" in p ? `counter(${p.field})` : cssString(p.text)).join(" ");
|
|
@@ -3449,7 +3695,7 @@ ${[2, 3, 4, 5, 6].map((l) => `.doc-toc .toc-l${l}{padding-left:${(l - 1) * 1.2}e
|
|
|
3449
3695
|
}
|
|
3450
3696
|
function documentToHtml(tree, options = {}) {
|
|
3451
3697
|
const s = tree.settings;
|
|
3452
|
-
const x = { tree, images: options.images };
|
|
3698
|
+
const x = { tree, images: options.images, ...options.srcFor ? { srcFor: options.srcFor } : {} };
|
|
3453
3699
|
const sections = tree.sections.map((sec) => {
|
|
3454
3700
|
const cls = [
|
|
3455
3701
|
"doc-section",
|
|
@@ -3472,8 +3718,8 @@ ${s.author ? `<meta name="author" content="${escapeHtml(s.author)}">` : ""}
|
|
|
3472
3718
|
</head>
|
|
3473
3719
|
<body>
|
|
3474
3720
|
<main class="doc">
|
|
3475
|
-
${s.cover ? coverHtml(s, x) : ""}
|
|
3476
|
-
${sections}
|
|
3721
|
+
${(options.transformBody ?? ((h) => h))(`${s.cover ? coverHtml(s, x) : ""}
|
|
3722
|
+
${sections}`)}
|
|
3477
3723
|
</main>
|
|
3478
3724
|
</body>
|
|
3479
3725
|
</html>`;
|
|
@@ -4812,6 +5058,7 @@ import { gfm } from "micromark-extension-gfm";
|
|
|
4812
5058
|
|
|
4813
5059
|
// src/markdown.ts
|
|
4814
5060
|
import { escapeHtml as escapeHtml3 } from "@ai-matrx/kit/html-escape";
|
|
5061
|
+
import { findTableEnd, rowCells, tableStartsAt, unescapeCellPipes } from "@ai-matrx/content-ir/source";
|
|
4815
5062
|
function removeThinkingContent(content) {
|
|
4816
5063
|
if (!content) return "";
|
|
4817
5064
|
const hide = (_match, before, after) => before.length >= 2 || after.length >= 2 ? "\n\n" : "\n";
|
|
@@ -4884,6 +5131,7 @@ function looksLikeJson(body) {
|
|
|
4884
5131
|
return false;
|
|
4885
5132
|
}
|
|
4886
5133
|
}
|
|
5134
|
+
var ALLOWED_TAGS = new Set(PRINT_HTML_SCHEMA.tagNames ?? []);
|
|
4887
5135
|
var LINK_URL = String.raw`((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?`;
|
|
4888
5136
|
var IMAGE_RE = new RegExp(String.raw`!\[([^\]]*)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
4889
5137
|
var LINK_RE = new RegExp(String.raw`\[([^\]]+)\]\(` + LINK_URL + String.raw`\)`, "g");
|