@ai-matrx/print 0.8.7 → 0.8.8
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 +25 -0
- package/dist/document.cjs +204 -3
- package/dist/document.cjs.map +1 -1
- package/dist/document.js +204 -3
- package/dist/document.js.map +1 -1
- package/dist/markdown.cjs +113 -6
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +113 -6
- package/dist/markdown.js.map +1 -1
- package/dist/safe-html.cjs +231 -0
- package/dist/safe-html.cjs.map +1 -0
- package/dist/safe-html.d.cts +43 -0
- package/dist/safe-html.d.ts +43 -0
- package/dist/safe-html.js +210 -0
- package/dist/safe-html.js.map +1 -0
- package/package.json +17 -3
package/dist/markdown.cjs
CHANGED
|
@@ -68,6 +68,103 @@ function openPrintWindow(htmlDocument, filename = "print", windowFeatures = "wid
|
|
|
68
68
|
// src/markdown.ts
|
|
69
69
|
var import_html_escape2 = require("@ai-matrx/kit/html-escape");
|
|
70
70
|
|
|
71
|
+
// src/safe-html.ts
|
|
72
|
+
var import_hast_util_from_html = require("hast-util-from-html");
|
|
73
|
+
var import_hast_util_sanitize = require("hast-util-sanitize");
|
|
74
|
+
var import_hast_util_to_html = require("hast-util-to-html");
|
|
75
|
+
var STRIP_WITH_CONTENT = [
|
|
76
|
+
"script",
|
|
77
|
+
"style",
|
|
78
|
+
"noscript",
|
|
79
|
+
"template",
|
|
80
|
+
"iframe",
|
|
81
|
+
"object",
|
|
82
|
+
"embed",
|
|
83
|
+
"textarea",
|
|
84
|
+
"select",
|
|
85
|
+
"head",
|
|
86
|
+
"title"
|
|
87
|
+
];
|
|
88
|
+
var FORBIDDEN_TAGS = [
|
|
89
|
+
...STRIP_WITH_CONTENT,
|
|
90
|
+
"form",
|
|
91
|
+
"meta",
|
|
92
|
+
"base",
|
|
93
|
+
"link",
|
|
94
|
+
"frame",
|
|
95
|
+
"frameset",
|
|
96
|
+
"applet",
|
|
97
|
+
"button",
|
|
98
|
+
"svg",
|
|
99
|
+
"math"
|
|
100
|
+
];
|
|
101
|
+
var baseAttributes = import_hast_util_sanitize.defaultSchema.attributes ?? {};
|
|
102
|
+
var SAFE_HTML_SCHEMA = {
|
|
103
|
+
...import_hast_util_sanitize.defaultSchema,
|
|
104
|
+
tagNames: (import_hast_util_sanitize.defaultSchema.tagNames ?? []).filter((t) => !FORBIDDEN_TAGS.includes(t)),
|
|
105
|
+
strip: STRIP_WITH_CONTENT,
|
|
106
|
+
// Authored anchors keep their names; the GitHub `user-content-` rewrite
|
|
107
|
+
// would also break `<label for>` → `<input id>` pairs.
|
|
108
|
+
clobberPrefix: "",
|
|
109
|
+
clobber: []
|
|
110
|
+
};
|
|
111
|
+
var PRINT_CLASS = /^(?:(?:matrx|print|code|doc|language)-[\w-]*|inline-code|is-checked)$/;
|
|
112
|
+
function withPrintClasses(defs = []) {
|
|
113
|
+
let found = false;
|
|
114
|
+
const out = defs.map((d) => {
|
|
115
|
+
if (Array.isArray(d) && d[0] === "className") {
|
|
116
|
+
found = true;
|
|
117
|
+
return [...d, PRINT_CLASS];
|
|
118
|
+
}
|
|
119
|
+
return d;
|
|
120
|
+
});
|
|
121
|
+
return found ? out : [...out, ["className", PRINT_CLASS]];
|
|
122
|
+
}
|
|
123
|
+
var printAttributes = Object.fromEntries(
|
|
124
|
+
Object.entries(baseAttributes).map(([tag, defs]) => [tag, withPrintClasses(defs)])
|
|
125
|
+
);
|
|
126
|
+
var cellAlign = ["style", /^text-align:\s*(?:left|center|right);?$/];
|
|
127
|
+
var PRINT_HTML_SCHEMA = {
|
|
128
|
+
...SAFE_HTML_SCHEMA,
|
|
129
|
+
tagNames: [
|
|
130
|
+
.../* @__PURE__ */ new Set([...SAFE_HTML_SCHEMA.tagNames ?? [], "section", "figure", "figcaption", "label"])
|
|
131
|
+
],
|
|
132
|
+
attributes: {
|
|
133
|
+
...printAttributes,
|
|
134
|
+
"*": [
|
|
135
|
+
...baseAttributes["*"] ?? [],
|
|
136
|
+
["className", PRINT_CLASS],
|
|
137
|
+
"role",
|
|
138
|
+
"ariaLabel",
|
|
139
|
+
"ariaHidden",
|
|
140
|
+
"dataLabel",
|
|
141
|
+
"dataLanguage",
|
|
142
|
+
"dataArtifactType"
|
|
143
|
+
],
|
|
144
|
+
// Table-cell alignment is the one inline style the converter writes.
|
|
145
|
+
td: withPrintClasses([...baseAttributes["td"] ?? [], [...cellAlign]]),
|
|
146
|
+
th: withPrintClasses([...baseAttributes["th"] ?? [], [...cellAlign]]),
|
|
147
|
+
// The flashcard flip is a checkbox + label pair (GitHub forces `disabled`).
|
|
148
|
+
input: withPrintClasses([["type", "checkbox"], "checked"]),
|
|
149
|
+
label: withPrintClasses(["htmlFor"])
|
|
150
|
+
},
|
|
151
|
+
required: { input: { type: "checkbox" } },
|
|
152
|
+
protocols: {
|
|
153
|
+
...import_hast_util_sanitize.defaultSchema.protocols,
|
|
154
|
+
href: [...import_hast_util_sanitize.defaultSchema.protocols?.href ?? [], "tel"],
|
|
155
|
+
// Embedded images (`data:image/png;base64,…`) — an <img> never runs script.
|
|
156
|
+
src: [...import_hast_util_sanitize.defaultSchema.protocols?.src ?? [], "data"]
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function sanitizeHtmlFragment(html, schema = SAFE_HTML_SCHEMA) {
|
|
160
|
+
if (!html) return "";
|
|
161
|
+
const tree = (0, import_hast_util_sanitize.sanitize)((0, import_hast_util_from_html.fromHtml)(html, { fragment: true }), schema);
|
|
162
|
+
return (0, import_hast_util_to_html.toHtml)(tree, { characterReferences: { useNamedReferences: true } });
|
|
163
|
+
}
|
|
164
|
+
function sanitizePrintHtml(html) {
|
|
165
|
+
return sanitizeHtmlFragment(html, PRINT_HTML_SCHEMA);
|
|
166
|
+
}
|
|
167
|
+
|
|
71
168
|
// src/markdown-css.ts
|
|
72
169
|
var MARKDOWN_DOCUMENT_TOKENS_CSS = `:root {
|
|
73
170
|
--matrx-print-accent: #667eea;
|
|
@@ -1407,11 +1504,11 @@ function protectRegions(markdown, g, stash, ctx) {
|
|
|
1407
1504
|
text = next;
|
|
1408
1505
|
}
|
|
1409
1506
|
text = sanitizeRawTags(text);
|
|
1410
|
-
if (text !== before && !ctx.strippedAnnounced) {
|
|
1507
|
+
if ((text !== before || hasDisallowedTag(text)) && !ctx.strippedAnnounced) {
|
|
1411
1508
|
ctx.strippedAnnounced = true;
|
|
1412
1509
|
ctx.options.onNotice?.({
|
|
1413
1510
|
code: "html-stripped",
|
|
1414
|
-
message: "
|
|
1511
|
+
message: "Markup that is not on the safe list (a <script>, <style>, <iframe>, <form>, <object>/<embed>, <meta>, <link>, an on\u2026 event handler, inline style, or a javascript: URL) was removed from the rendered copy.",
|
|
1415
1512
|
remedy: 'To show a full HTML page, put it in an ```html fence or an <artifact type="html"> block \u2014 it is printed as its source.'
|
|
1416
1513
|
});
|
|
1417
1514
|
}
|
|
@@ -1423,6 +1520,14 @@ ${stash.block(flashcardsHtml(g, cards, ctx))}
|
|
|
1423
1520
|
});
|
|
1424
1521
|
return text;
|
|
1425
1522
|
}
|
|
1523
|
+
var ALLOWED_TAGS = new Set(PRINT_HTML_SCHEMA.tagNames ?? []);
|
|
1524
|
+
function hasDisallowedTag(text) {
|
|
1525
|
+
for (const m of text.matchAll(/<\/?([a-zA-Z][\w:-]*)/g)) {
|
|
1526
|
+
const tag = m[1].toLowerCase();
|
|
1527
|
+
if (tag !== "flashcards" && !ALLOWED_TAGS.has(tag)) return true;
|
|
1528
|
+
}
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1426
1531
|
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
1427
1532
|
function decodeForSchemeCheck(value) {
|
|
1428
1533
|
return value.replace(/&#x([0-9a-f]+);?/gi, (_m, h) => String.fromCharCode(parseInt(h, 16))).replace(/&#(\d+);?/g, (_m, d) => String.fromCharCode(Number(d))).replace(/:/gi, ":").replace(/[\u0000-\u0020]+/g, "").toLowerCase();
|
|
@@ -1477,7 +1582,7 @@ function splitTableRow(row) {
|
|
|
1477
1582
|
if (r.endsWith("|") && !r.endsWith("\\|")) r = r.slice(0, -1);
|
|
1478
1583
|
return r.split(/(?<!\\)\|/).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
1479
1584
|
}
|
|
1480
|
-
function
|
|
1585
|
+
function cellAlign2(separator) {
|
|
1481
1586
|
const s = separator.trim();
|
|
1482
1587
|
const left = s.startsWith(":");
|
|
1483
1588
|
const right = s.endsWith(":");
|
|
@@ -1491,7 +1596,7 @@ function convertTables(html, g) {
|
|
|
1491
1596
|
tableRegex,
|
|
1492
1597
|
(_m, headerRow, separatorRow, bodyRows) => {
|
|
1493
1598
|
const headers = splitTableRow(headerRow);
|
|
1494
|
-
const aligns = splitTableRow(separatorRow).map(
|
|
1599
|
+
const aligns = splitTableRow(separatorRow).map(cellAlign2);
|
|
1495
1600
|
const rows = bodyRows.trim().split("\n").filter((r) => r.trim()).map(splitTableRow);
|
|
1496
1601
|
let t = `<table${cls(g.table)}>
|
|
1497
1602
|
<thead${cls(g.thead)}>
|
|
@@ -1605,7 +1710,9 @@ function markdownToHtml(markdown, options = {}) {
|
|
|
1605
1710
|
return "";
|
|
1606
1711
|
}
|
|
1607
1712
|
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1608
|
-
return
|
|
1713
|
+
return sanitizePrintHtml(
|
|
1714
|
+
`<div class="matrx-content-container">${documentBody(source, newContext(options))}</div>`
|
|
1715
|
+
);
|
|
1609
1716
|
}
|
|
1610
1717
|
function documentBody(markdown, ctx) {
|
|
1611
1718
|
const g = DOCUMENT_GRAMMAR;
|
|
@@ -1675,7 +1782,7 @@ function markdownToArticleHtml(markdown, options = {}) {
|
|
|
1675
1782
|
return "";
|
|
1676
1783
|
}
|
|
1677
1784
|
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1678
|
-
return articleBody(source, newContext(options));
|
|
1785
|
+
return sanitizePrintHtml(articleBody(source, newContext(options)));
|
|
1679
1786
|
}
|
|
1680
1787
|
function articleBody(markdown, ctx) {
|
|
1681
1788
|
const g = ARTICLE_GRAMMAR;
|