@ai-matrx/print 0.8.6 → 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 +35 -0
- package/dist/document.cjs +206 -4
- package/dist/document.cjs.map +1 -1
- package/dist/document.js +206 -4
- package/dist/document.js.map +1 -1
- package/dist/markdown.cjs +115 -7
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +115 -7
- 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 +15 -1
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;
|
|
@@ -1098,7 +1195,8 @@ function splitAtPageBreaks(markdown) {
|
|
|
1098
1195
|
// src/markdown.ts
|
|
1099
1196
|
function removeThinkingContent(content) {
|
|
1100
1197
|
if (!content) return "";
|
|
1101
|
-
|
|
1198
|
+
const hide = (_match, before, after) => before.length >= 2 || after.length >= 2 ? "\n\n" : "\n";
|
|
1199
|
+
let result = content.replace(/(\n*)<thinking>[\s\S]*?<\/thinking>(\n*)/gi, hide).replace(/(\n*)<think>[\s\S]*?<\/think>(\n*)/gi, hide).replace(/(\n*)<reasoning>[\s\S]*?<\/reasoning>(\n*)/gi, hide);
|
|
1102
1200
|
result = result.replace(/\n{3,}/g, "\n\n");
|
|
1103
1201
|
return result.trim();
|
|
1104
1202
|
}
|
|
@@ -1406,11 +1504,11 @@ function protectRegions(markdown, g, stash, ctx) {
|
|
|
1406
1504
|
text = next;
|
|
1407
1505
|
}
|
|
1408
1506
|
text = sanitizeRawTags(text);
|
|
1409
|
-
if (text !== before && !ctx.strippedAnnounced) {
|
|
1507
|
+
if ((text !== before || hasDisallowedTag(text)) && !ctx.strippedAnnounced) {
|
|
1410
1508
|
ctx.strippedAnnounced = true;
|
|
1411
1509
|
ctx.options.onNotice?.({
|
|
1412
1510
|
code: "html-stripped",
|
|
1413
|
-
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.",
|
|
1414
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.'
|
|
1415
1513
|
});
|
|
1416
1514
|
}
|
|
@@ -1422,6 +1520,14 @@ ${stash.block(flashcardsHtml(g, cards, ctx))}
|
|
|
1422
1520
|
});
|
|
1423
1521
|
return text;
|
|
1424
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
|
+
}
|
|
1425
1531
|
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
1426
1532
|
function decodeForSchemeCheck(value) {
|
|
1427
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();
|
|
@@ -1476,7 +1582,7 @@ function splitTableRow(row) {
|
|
|
1476
1582
|
if (r.endsWith("|") && !r.endsWith("\\|")) r = r.slice(0, -1);
|
|
1477
1583
|
return r.split(/(?<!\\)\|/).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
1478
1584
|
}
|
|
1479
|
-
function
|
|
1585
|
+
function cellAlign2(separator) {
|
|
1480
1586
|
const s = separator.trim();
|
|
1481
1587
|
const left = s.startsWith(":");
|
|
1482
1588
|
const right = s.endsWith(":");
|
|
@@ -1490,7 +1596,7 @@ function convertTables(html, g) {
|
|
|
1490
1596
|
tableRegex,
|
|
1491
1597
|
(_m, headerRow, separatorRow, bodyRows) => {
|
|
1492
1598
|
const headers = splitTableRow(headerRow);
|
|
1493
|
-
const aligns = splitTableRow(separatorRow).map(
|
|
1599
|
+
const aligns = splitTableRow(separatorRow).map(cellAlign2);
|
|
1494
1600
|
const rows = bodyRows.trim().split("\n").filter((r) => r.trim()).map(splitTableRow);
|
|
1495
1601
|
let t = `<table${cls(g.table)}>
|
|
1496
1602
|
<thead${cls(g.thead)}>
|
|
@@ -1604,7 +1710,9 @@ function markdownToHtml(markdown, options = {}) {
|
|
|
1604
1710
|
return "";
|
|
1605
1711
|
}
|
|
1606
1712
|
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1607
|
-
return
|
|
1713
|
+
return sanitizePrintHtml(
|
|
1714
|
+
`<div class="matrx-content-container">${documentBody(source, newContext(options))}</div>`
|
|
1715
|
+
);
|
|
1608
1716
|
}
|
|
1609
1717
|
function documentBody(markdown, ctx) {
|
|
1610
1718
|
const g = DOCUMENT_GRAMMAR;
|
|
@@ -1674,7 +1782,7 @@ function markdownToArticleHtml(markdown, options = {}) {
|
|
|
1674
1782
|
return "";
|
|
1675
1783
|
}
|
|
1676
1784
|
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1677
|
-
return articleBody(source, newContext(options));
|
|
1785
|
+
return sanitizePrintHtml(articleBody(source, newContext(options)));
|
|
1678
1786
|
}
|
|
1679
1787
|
function articleBody(markdown, ctx) {
|
|
1680
1788
|
const g = ARTICLE_GRAMMAR;
|