@ai-matrx/print 0.8.1 → 0.8.3
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 +22 -0
- package/dist/document.cjs +132 -8
- package/dist/document.cjs.map +1 -1
- package/dist/document.d.cts +16 -2
- package/dist/document.d.ts +16 -2
- package/dist/document.js +132 -8
- package/dist/document.js.map +1 -1
- package/dist/markdown.cjs +40 -1
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.d.cts +9 -1
- package/dist/markdown.d.ts +9 -1
- package/dist/markdown.js +40 -1
- package/dist/markdown.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.3
|
|
4
|
+
|
|
5
|
+
- **Fixed:** an `<artifact>` envelope (how chat answers store tables and code)
|
|
6
|
+
printed as raw pipes in documents. The document pipeline now unwraps it with
|
|
7
|
+
the same rules the print skins use (`unwrapArtifactsToMarkdown`, exported
|
|
8
|
+
from `/markdown`): a table stays a table, code stays code.
|
|
9
|
+
- **Fixed (EPUB):** a `$$…$$` formula inside a sentence rendered as a `<div>`
|
|
10
|
+
inside `<p>` (epubcheck RSC-005); it is now a block-styled span.
|
|
11
|
+
- **Word:** East-Asian text carries a CJK font hint, so Word/LibreOffice pick
|
|
12
|
+
an installed Japanese/Chinese face.
|
|
13
|
+
- Consumer action: none — refresh to latest.
|
|
14
|
+
|
|
15
|
+
## 0.8.2
|
|
16
|
+
|
|
17
|
+
- **PDF font fallback covers Japanese / Chinese / Korean.** Characters the
|
|
18
|
+
body, emoji and symbol faces cannot draw now fall back to Noto Sans JP
|
|
19
|
+
(SIL OFL, fontsource) — loading only the unicode-range subsets the document
|
|
20
|
+
uses (`fonts.cjk` loader; default jsDelivr). Found on a real conversation
|
|
21
|
+
whose citations carried Japanese source titles. A glyph is announced only
|
|
22
|
+
when no face in the chain can draw it.
|
|
23
|
+
- Consumer action: none — refresh to latest.
|
|
24
|
+
|
|
3
25
|
## 0.8.1
|
|
4
26
|
|
|
5
27
|
- **Fixed (Word):** exporting DOCX laid out a whole PDF to number the table of
|
package/dist/document.cjs
CHANGED
|
@@ -35,12 +35,14 @@ __export(document_exports, {
|
|
|
35
35
|
CitationEngine: () => CitationEngine,
|
|
36
36
|
DEFAULT_FOOTER: () => DEFAULT_FOOTER,
|
|
37
37
|
DOCUMENT_FORMATS: () => DOCUMENT_FORMATS,
|
|
38
|
+
NOTO_CJK_BASE: () => NOTO_CJK_BASE,
|
|
38
39
|
NOTO_EMOJI_URL: () => NOTO_EMOJI_URL,
|
|
39
40
|
NOTO_SYMBOLS_URL: () => NOTO_SYMBOLS_URL,
|
|
40
41
|
PAGE_SIZES: () => PAGE_SIZES,
|
|
41
42
|
buildPdfDefinition: () => buildPdfDefinition,
|
|
42
43
|
countUnplacedWords: () => countUnplacedWords,
|
|
43
44
|
cslStyleId: () => cslStyleId,
|
|
45
|
+
defaultCjkFontLoader: () => defaultCjkFontLoader,
|
|
44
46
|
defaultCslFetcher: () => defaultCslFetcher,
|
|
45
47
|
defaultEmojiFontLoader: () => defaultEmojiFontLoader,
|
|
46
48
|
defaultImageLoader: () => defaultImageLoader,
|
|
@@ -63,6 +65,7 @@ __export(document_exports, {
|
|
|
63
65
|
parseLength: () => parseLength,
|
|
64
66
|
parseLocator: () => parseLocator,
|
|
65
67
|
parseMathML: () => parseMathML,
|
|
68
|
+
parseUnicodeRangeCss: () => parseUnicodeRangeCss,
|
|
66
69
|
plainText: () => plainText,
|
|
67
70
|
prepareDocument: () => prepareDocument,
|
|
68
71
|
renderBlocksHtml: () => renderBlocksHtml,
|
|
@@ -3104,7 +3107,11 @@ async function documentToDocx(tree, options = {}) {
|
|
|
3104
3107
|
numbering: { config: [...c.numbering.entries()].map(([reference, levels2]) => ({ reference, levels: levels2 })) },
|
|
3105
3108
|
styles: {
|
|
3106
3109
|
default: {
|
|
3107
|
-
|
|
3110
|
+
// East-Asian text gets a CJK face (Word and LibreOffice substitute an installed one).
|
|
3111
|
+
document: {
|
|
3112
|
+
run: { font: { ascii: "Cambria", hAnsi: "Cambria", cs: "Cambria", eastAsia: "Yu Gothic" }, size, color: HEX.text },
|
|
3113
|
+
paragraph: { spacing: { after: 140, line: 300 } }
|
|
3114
|
+
}
|
|
3108
3115
|
},
|
|
3109
3116
|
paragraphStyles: [
|
|
3110
3117
|
heading("Heading1", "Heading 1", s.fontSize * 1.8, 0),
|
|
@@ -3247,7 +3254,7 @@ function renderInlineHtml(nodes, x) {
|
|
|
3247
3254
|
case "cite":
|
|
3248
3255
|
return n.resolved ? `<a class="doc-cite" href="${(0, import_html_escape.escapeHtml)(href("ref-" + (n.keys[0] ?? ""), x))}">${(0, import_html_escape.escapeHtml)(n.label)}</a>` : `<span class="doc-cite doc-cite-missing">${(0, import_html_escape.escapeHtml)(n.label)}</span>`;
|
|
3249
3256
|
case "math":
|
|
3250
|
-
return mathHtml(n.tex, n.display, x);
|
|
3257
|
+
return mathHtml(n.tex, n.display, x, true);
|
|
3251
3258
|
case "noteRef":
|
|
3252
3259
|
return `<sup class="doc-note-ref"><a href="${(0, import_html_escape.escapeHtml)(href(`note-${n.n}`, x))}" id="noteref-${n.n}">${n.n}</a></sup>`;
|
|
3253
3260
|
default:
|
|
@@ -3255,10 +3262,11 @@ function renderInlineHtml(nodes, x) {
|
|
|
3255
3262
|
}
|
|
3256
3263
|
}).join("");
|
|
3257
3264
|
}
|
|
3258
|
-
function mathHtml(tex, display, x) {
|
|
3265
|
+
function mathHtml(tex, display, x, inText = false) {
|
|
3259
3266
|
const m = x.tree.math.get(mathKey(tex, display));
|
|
3260
3267
|
if (m?.mathml && !m.error) {
|
|
3261
|
-
|
|
3268
|
+
if (!display) return `<span class="doc-math">${m.mathml}</span>`;
|
|
3269
|
+
return inText ? `<span class="doc-math-display">${m.mathml}</span>` : `<div class="doc-math-display">${m.mathml}</div>`;
|
|
3262
3270
|
}
|
|
3263
3271
|
return `<code class="doc-math-tex">${(0, import_html_escape.escapeHtml)(display ? `$$${tex}$$` : `$${tex}$`)}</code>`;
|
|
3264
3272
|
}
|
|
@@ -3420,7 +3428,7 @@ table caption{caption-side:top;text-align:left;margin-bottom:4pt;}
|
|
|
3420
3428
|
.doc-figure figcaption{margin-top:4pt;}
|
|
3421
3429
|
.doc-image-missing{border:1px dashed var(--matrx-print-rule,#d1d5db);padding:16pt;color:var(--matrx-print-muted,#6b7280);}
|
|
3422
3430
|
.doc-inline-img{max-height:1.2em;vertical-align:middle;}
|
|
3423
|
-
.doc-math-display{margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
|
|
3431
|
+
.doc-math-display{display:block;margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
|
|
3424
3432
|
.doc-math-display math{font-size:1.15em;}
|
|
3425
3433
|
.doc-math-tex{color:var(--matrx-print-danger,#b91c1c);}
|
|
3426
3434
|
.doc-task-list{list-style:none;padding-left:0.2em;}
|
|
@@ -3499,7 +3507,7 @@ blockquote{margin:0 0 0.8em;padding-left:1em;border-left:3px solid #d1d5db;color
|
|
|
3499
3507
|
.doc-cover-logo{max-height:5em;}
|
|
3500
3508
|
.doc-cover-title{font-size:2em;}
|
|
3501
3509
|
.matrx-page-break{page-break-after:always;break-after:page;}
|
|
3502
|
-
.doc-math-display{margin:0.6em 0;text-align:center;}
|
|
3510
|
+
.doc-math-display{display:block;margin:0.6em 0;text-align:center;}
|
|
3503
3511
|
`.trim();
|
|
3504
3512
|
function extOf(mime) {
|
|
3505
3513
|
return mime === "image/png" ? "png" : mime === "image/gif" ? "gif" : "jpg";
|
|
@@ -4533,12 +4541,13 @@ async function loadTypography(tree, loaders = {}) {
|
|
|
4533
4541
|
const base = await loadFaces(vfs, s.fontSize);
|
|
4534
4542
|
let needEmoji = false;
|
|
4535
4543
|
let needSymbols = false;
|
|
4544
|
+
const cjk = /* @__PURE__ */ new Set();
|
|
4536
4545
|
for (const ch of documentText(tree)) {
|
|
4537
4546
|
const cp = ch.codePointAt(0);
|
|
4538
4547
|
if (cp < 32 || cp === 8205 || cp === 65039 || base.body(cp)) continue;
|
|
4539
4548
|
if (EMOJI_CP.test(ch)) needEmoji = true;
|
|
4549
|
+
else if (isCjk(cp)) cjk.add(cp);
|
|
4540
4550
|
else needSymbols = true;
|
|
4541
|
-
if (needEmoji && needSymbols) break;
|
|
4542
4551
|
}
|
|
4543
4552
|
const fallbacks = [];
|
|
4544
4553
|
if (needEmoji) {
|
|
@@ -4549,6 +4558,26 @@ async function loadTypography(tree, loaders = {}) {
|
|
|
4549
4558
|
const b = await registerFace("NotoSymbols", "NotoSansMath.woff", loaders.symbols ?? defaultSymbolFontLoader, tree, "symbols");
|
|
4550
4559
|
if (b) fallbacks.push({ font: "NotoSymbols", bytes: b });
|
|
4551
4560
|
}
|
|
4561
|
+
if (cjk.size) {
|
|
4562
|
+
let subsets = [];
|
|
4563
|
+
try {
|
|
4564
|
+
subsets = await (loaders.cjk ?? defaultCjkFontLoader)([...cjk]);
|
|
4565
|
+
} catch {
|
|
4566
|
+
subsets = [];
|
|
4567
|
+
}
|
|
4568
|
+
for (const sub of subsets) {
|
|
4569
|
+
const font = `NotoCJK_${sub.name.replace(/[^\w]/g, "_")}`;
|
|
4570
|
+
const b = await registerFace(font, `${sub.name}.woff`, async () => sub.bytes, tree, "Japanese / Chinese");
|
|
4571
|
+
if (b) fallbacks.push({ font, bytes: b });
|
|
4572
|
+
}
|
|
4573
|
+
if (!subsets.length) {
|
|
4574
|
+
tree.notices.push({
|
|
4575
|
+
code: "glyph-substituted",
|
|
4576
|
+
message: "The Japanese / Chinese font could not be loaded, so those characters were left out of the PDF (Word, EPUB and HTML keep them).",
|
|
4577
|
+
remedy: "Check the internet connection (the font comes from cdn.jsdelivr.net) and export again."
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4552
4581
|
const faces = fallbacks.length ? await loadFaces(vfs, s.fontSize, fallbacks) : base;
|
|
4553
4582
|
if (!s.hyphenate) return { faces };
|
|
4554
4583
|
const split = await loadHyphenator(s.lang);
|
|
@@ -4571,6 +4600,37 @@ var urlLoader = (url) => async () => {
|
|
|
4571
4600
|
};
|
|
4572
4601
|
var defaultEmojiFontLoader = urlLoader(NOTO_EMOJI_URL);
|
|
4573
4602
|
var defaultSymbolFontLoader = urlLoader(NOTO_SYMBOLS_URL);
|
|
4603
|
+
var NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
|
|
4604
|
+
function parseUnicodeRangeCss(css) {
|
|
4605
|
+
const out = [];
|
|
4606
|
+
for (const block3 of css.split("@font-face").slice(1)) {
|
|
4607
|
+
const file = /url\(\.\/files\/([\w-]+)\.woff\)/.exec(block3)?.[1];
|
|
4608
|
+
const range = /unicode-range:\s*([^;]+);/.exec(block3)?.[1];
|
|
4609
|
+
if (!file || !range) continue;
|
|
4610
|
+
const ranges = range.split(",").map((r) => {
|
|
4611
|
+
const [a, b] = r.trim().replace(/^U\+/i, "").split("-");
|
|
4612
|
+
const lo = parseInt(a, 16);
|
|
4613
|
+
return [lo, b ? parseInt(b, 16) : lo];
|
|
4614
|
+
});
|
|
4615
|
+
out.push({ file, ranges });
|
|
4616
|
+
}
|
|
4617
|
+
return out;
|
|
4618
|
+
}
|
|
4619
|
+
var defaultCjkFontLoader = async (cps) => {
|
|
4620
|
+
if (typeof fetch !== "function" || cps.length === 0) return [];
|
|
4621
|
+
const res = await fetch(`${NOTO_CJK_BASE}/400.css`);
|
|
4622
|
+
if (!res.ok) return [];
|
|
4623
|
+
const faces = parseUnicodeRangeCss(await res.text()).filter((f) => cps.some((cp) => f.ranges.some(([a, b]) => cp >= a && cp <= b)));
|
|
4624
|
+
const out = [];
|
|
4625
|
+
await Promise.all(
|
|
4626
|
+
faces.map(async (f) => {
|
|
4627
|
+
const r = await fetch(`${NOTO_CJK_BASE}/files/${f.file}.woff`);
|
|
4628
|
+
if (r.ok) out.push({ name: f.file, bytes: new Uint8Array(await r.arrayBuffer()) });
|
|
4629
|
+
})
|
|
4630
|
+
);
|
|
4631
|
+
return out;
|
|
4632
|
+
};
|
|
4633
|
+
var isCjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 201551;
|
|
4574
4634
|
var EMOJI_CP = /\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20E3/u;
|
|
4575
4635
|
function documentText(tree) {
|
|
4576
4636
|
const s = tree.settings;
|
|
@@ -4737,6 +4797,70 @@ function removeThinkingContent(content) {
|
|
|
4737
4797
|
return result.trim();
|
|
4738
4798
|
}
|
|
4739
4799
|
var PAGE_BREAK_HTML = `<div class="${PAGE_BREAK_CLASS}" role="separator" aria-label="${PAGE_BREAK_LABEL}" data-label="${PAGE_BREAK_LABEL}"></div>`;
|
|
4800
|
+
var CODE_ARTIFACT_LANGUAGE = {
|
|
4801
|
+
html: "html",
|
|
4802
|
+
react: "tsx",
|
|
4803
|
+
svg: "svg",
|
|
4804
|
+
mermaid: "mermaid",
|
|
4805
|
+
diff: "diff",
|
|
4806
|
+
iframe: "html",
|
|
4807
|
+
code: ""
|
|
4808
|
+
};
|
|
4809
|
+
function parseTagAttributes(tag) {
|
|
4810
|
+
const attrs = {};
|
|
4811
|
+
for (const m of tag.matchAll(/([a-zA-Z_][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
|
|
4812
|
+
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? "";
|
|
4813
|
+
}
|
|
4814
|
+
return attrs;
|
|
4815
|
+
}
|
|
4816
|
+
function unwrapArtifactsToMarkdown(markdown) {
|
|
4817
|
+
if (!/<artifact\b/i.test(markdown)) return markdown;
|
|
4818
|
+
return markdown.split(/(^[ \t]*(?:`{3,}|~{3,})[\s\S]*?^[ \t]*(?:`{3,}|~{3,})[ \t]*$)/m).map(
|
|
4819
|
+
(part, i) => i % 2 === 1 ? part : part.replace(/<artifact\b((?:[^>"']|"[^"]*"|'[^']*')*?)>([\s\S]*?)<\/artifact>/gi, (_m, tag, body) => {
|
|
4820
|
+
const attrs = parseTagAttributes(tag);
|
|
4821
|
+
const type = (attrs.type ?? "").toLowerCase();
|
|
4822
|
+
const trimmed = body.replace(/^\n+|\n+$/g, "");
|
|
4823
|
+
if (!trimmed.trim()) return "";
|
|
4824
|
+
if (/^\s*(`{3,}|~{3,})/.test(trimmed)) return `
|
|
4825
|
+
|
|
4826
|
+
${trimmed}
|
|
4827
|
+
|
|
4828
|
+
`;
|
|
4829
|
+
if (type in CODE_ARTIFACT_LANGUAGE) {
|
|
4830
|
+
const lang = CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || "";
|
|
4831
|
+
return `
|
|
4832
|
+
|
|
4833
|
+
\`\`\`\`${lang}
|
|
4834
|
+
${trimmed}
|
|
4835
|
+
\`\`\`\`
|
|
4836
|
+
|
|
4837
|
+
`;
|
|
4838
|
+
}
|
|
4839
|
+
if (looksLikeJson(trimmed)) return `
|
|
4840
|
+
|
|
4841
|
+
\`\`\`\`json
|
|
4842
|
+
${JSON.stringify(JSON.parse(trimmed), null, 2)}
|
|
4843
|
+
\`\`\`\`
|
|
4844
|
+
|
|
4845
|
+
`;
|
|
4846
|
+
return `
|
|
4847
|
+
|
|
4848
|
+
${trimmed}
|
|
4849
|
+
|
|
4850
|
+
`;
|
|
4851
|
+
})
|
|
4852
|
+
).join("");
|
|
4853
|
+
}
|
|
4854
|
+
function looksLikeJson(body) {
|
|
4855
|
+
const t = body.trim();
|
|
4856
|
+
if (!/^[[{]/.test(t)) return false;
|
|
4857
|
+
try {
|
|
4858
|
+
JSON.parse(t);
|
|
4859
|
+
return true;
|
|
4860
|
+
} catch {
|
|
4861
|
+
return false;
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4740
4864
|
var LINK_URL = String.raw`((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?`;
|
|
4741
4865
|
var IMAGE_RE = new RegExp(String.raw`!\[([^\]]*)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
4742
4866
|
var LINK_RE = new RegExp(String.raw`\[([^\]]+)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
@@ -5191,7 +5315,7 @@ function parseDocument(source, options = {}) {
|
|
|
5191
5315
|
const settings = resolveSettings(fm.data, sink, options);
|
|
5192
5316
|
for (const n of sink) notify(n);
|
|
5193
5317
|
const body = removeThinkingContent(fm.body);
|
|
5194
|
-
const lifted = extractMath(body);
|
|
5318
|
+
const lifted = extractMath(unwrapArtifactsToMarkdown(body));
|
|
5195
5319
|
const root = (0, import_mdast_util_from_markdown.fromMarkdown)(prepass(lifted.text), {
|
|
5196
5320
|
extensions: [(0, import_micromark_extension_gfm.gfm)()],
|
|
5197
5321
|
mdastExtensions: [(0, import_mdast_util_gfm.gfmFromMarkdown)()]
|