@ai-matrx/print 0.8.2 → 0.8.4

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.
@@ -322,14 +322,22 @@ interface FallbackFontLoaders {
322
322
  emoji?: FontLoader | undefined;
323
323
  symbols?: FontLoader | undefined;
324
324
  cjk?: CjkFontLoader | undefined;
325
+ arabic?: FontLoader | undefined;
326
+ hebrew?: FontLoader | undefined;
325
327
  }
326
328
  /** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
327
329
  declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
330
+ /** Noto Sans KR (SIL OFL) — Hangul, same unicode-range scheme. */
331
+ declare const NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
332
+ /** Noto Sans Arabic / Hebrew (SIL OFL) — one script subset each. */
333
+ declare const NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
334
+ declare const NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
328
335
  /** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
329
336
  declare function parseUnicodeRangeCss(css: string): Array<{
330
337
  file: string;
331
338
  ranges: Array<[number, number]>;
332
339
  }>;
340
+ /** Hangul from Noto Sans KR, every other CJK character from Noto Sans JP. */
333
341
  declare const defaultCjkFontLoader: CjkFontLoader;
334
342
  /**
335
343
  * Words pdfmake did not put on a page: a text node with no position was never
@@ -416,6 +424,8 @@ declare class CitationEngine {
416
424
  /** A note style: every citation becomes a numbered note. */
417
425
  readonly isNote: boolean;
418
426
  private readonly numeric;
427
+ /** APA 7 §8.11: narrative citations join authors with "and", not "&". */
428
+ private readonly apaNarrativeAnd;
419
429
  constructor(styleXml: string, localeXml: string, items: CslItem[], lang: string);
420
430
  /** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
421
431
  cite(refs: CiteRef[], opts?: {
@@ -671,4 +681,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
671
681
  /** Browser helper: save an export through a download link. */
672
682
  declare function downloadDocumentExport(exp: DocumentExport): void;
673
683
 
674
- export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
684
+ export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_ARABIC_URL, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_HEBREW_URL, NOTO_KR_BASE, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
@@ -322,14 +322,22 @@ interface FallbackFontLoaders {
322
322
  emoji?: FontLoader | undefined;
323
323
  symbols?: FontLoader | undefined;
324
324
  cjk?: CjkFontLoader | undefined;
325
+ arabic?: FontLoader | undefined;
326
+ hebrew?: FontLoader | undefined;
325
327
  }
326
328
  /** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
327
329
  declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
330
+ /** Noto Sans KR (SIL OFL) — Hangul, same unicode-range scheme. */
331
+ declare const NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
332
+ /** Noto Sans Arabic / Hebrew (SIL OFL) — one script subset each. */
333
+ declare const NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
334
+ declare const NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
328
335
  /** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
329
336
  declare function parseUnicodeRangeCss(css: string): Array<{
330
337
  file: string;
331
338
  ranges: Array<[number, number]>;
332
339
  }>;
340
+ /** Hangul from Noto Sans KR, every other CJK character from Noto Sans JP. */
333
341
  declare const defaultCjkFontLoader: CjkFontLoader;
334
342
  /**
335
343
  * Words pdfmake did not put on a page: a text node with no position was never
@@ -416,6 +424,8 @@ declare class CitationEngine {
416
424
  /** A note style: every citation becomes a numbered note. */
417
425
  readonly isNote: boolean;
418
426
  private readonly numeric;
427
+ /** APA 7 §8.11: narrative citations join authors with "and", not "&". */
428
+ private readonly apaNarrativeAnd;
419
429
  constructor(styleXml: string, localeXml: string, items: CslItem[], lang: string);
420
430
  /** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
421
431
  cite(refs: CiteRef[], opts?: {
@@ -671,4 +681,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
671
681
  /** Browser helper: save an export through a download link. */
672
682
  declare function downloadDocumentExport(exp: DocumentExport): void;
673
683
 
674
- export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
684
+ export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_ARABIC_URL, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_HEBREW_URL, NOTO_KR_BASE, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
package/dist/document.js CHANGED
@@ -1,3 +1,68 @@
1
+ // src/document/bidi.ts
2
+ import bidiFactory from "bidi-js";
3
+ var BIDI = /* @__PURE__ */ Symbol.for("ai-matrx.print.document.bidi");
4
+ function bidi() {
5
+ const g = globalThis;
6
+ if (!g[BIDI]) g[BIDI] = bidiFactory();
7
+ return g[BIDI];
8
+ }
9
+ var RTL_RE = /[֐-ࣿיִ-﷿ﹰ-]/;
10
+ function isRtlParagraph(text) {
11
+ if (!RTL_RE.test(text)) return false;
12
+ return (bidi().getEmbeddingLevels(text).paragraphs[0]?.level ?? 0) % 2 === 1;
13
+ }
14
+ function visualOrder(input, rtl) {
15
+ const b = bidi();
16
+ const runs3 = input.map((r) => typeof r === "string" ? { text: r } : { ...r, text: String(r.text ?? "") });
17
+ const lines = [[]];
18
+ runs3.forEach((r, i) => {
19
+ for (const m of r.text.matchAll(/\n|[^\S\n]+|[\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]+|[^\s\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]+/g)) {
20
+ if (m[0] === "\n") lines.push([]);
21
+ else lines[lines.length - 1].push({ run: i, text: m[0] });
22
+ }
23
+ });
24
+ const out = [];
25
+ lines.forEach((line, li) => {
26
+ if (li > 0) out.push({ ...runs3[0], text: "\n" });
27
+ const text = line.map((t) => t.text).join("");
28
+ if (!text) return;
29
+ const { levels } = b.getEmbeddingLevels(text, rtl ? "rtl" : "ltr");
30
+ let at = 0;
31
+ const tokens = line.map((t) => {
32
+ const level = levels[at] ?? 0;
33
+ let s = t.text;
34
+ if (level % 2 === 1) {
35
+ s = [...s].map((ch) => b.getMirroredCharacter(ch) ?? ch).join("");
36
+ if (!RTL_RE.test(s) && !/^\d/.test(s)) s = [...s].reverse().join("");
37
+ }
38
+ at += t.text.length;
39
+ return { ...t, text: s, level };
40
+ });
41
+ const max = Math.max(...tokens.map((t) => t.level));
42
+ const minOdd = Math.min(...tokens.map((t) => t.level % 2 === 1 ? t.level : Infinity));
43
+ for (let lvl = max; lvl >= minOdd && Number.isFinite(minOdd); lvl--) {
44
+ for (let i = 0; i < tokens.length; ) {
45
+ if (tokens[i].level < lvl) {
46
+ i++;
47
+ continue;
48
+ }
49
+ let j = i;
50
+ while (j < tokens.length && tokens[j].level >= lvl) j++;
51
+ tokens.splice(i, j - i, ...tokens.slice(i, j).reverse());
52
+ i = j;
53
+ }
54
+ }
55
+ for (const t of tokens) {
56
+ const last = out[out.length - 1];
57
+ const src = runs3[t.run];
58
+ if (last && last.text !== "\n" && last.__run === t.run) last.text += t.text;
59
+ else out.push({ ...src, text: t.text, __run: t.run });
60
+ }
61
+ });
62
+ for (const r of out) delete r.__run;
63
+ return out;
64
+ }
65
+
1
66
  // src/document/math.ts
2
67
  import { pairDisplayMath, singleDollarMathEnd } from "@ai-matrx/content-ir/source";
3
68
  var MATH_OPEN = "\uE100";
@@ -2045,6 +2110,8 @@ var CitationEngine = class {
2045
2110
  /** A note style: every citation becomes a numbered note. */
2046
2111
  isNote;
2047
2112
  numeric;
2113
+ /** APA 7 §8.11: narrative citations join authors with "and", not "&". */
2114
+ apaNarrativeAnd;
2048
2115
  constructor(styleXml, localeXml, items, lang) {
2049
2116
  const byId = new Map(items.map((i) => [i.id, toCslJson(i)]));
2050
2117
  const sys = {
@@ -2055,6 +2122,7 @@ var CitationEngine = class {
2055
2122
  this.engine = new Engine(sys, styleXml, cslLocaleTag(lang), true);
2056
2123
  this.engine.setOutputFormat("html");
2057
2124
  this.isNote = this.engine.opt.xclass === "note";
2125
+ this.apaNarrativeAnd = /<title>[^<]*(?:\bAPA\b|American Psychological Association)/i.test(styleXml) || /<link[^>]*href="[^"]*\/styles\/apa(?:-[\w-]*)?"[^>]*rel="(?:self|template|independent-parent)"/i.test(styleXml);
2058
2126
  this.numeric = /citation-number/.test(styleXml.slice(styleXml.indexOf("<citation"), styleXml.indexOf("</citation>")));
2059
2127
  }
2060
2128
  /** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
@@ -2091,7 +2159,8 @@ var CitationEngine = class {
2091
2159
  if (opts.narrative && refs.length === 1 && !this.isNote && !this.numeric) {
2092
2160
  const author = make(items);
2093
2161
  const rest = make(refs.map((r, i) => ({ ...items[i], "author-only": void 0, "suppress-author": true, id: r.key })));
2094
- const join = () => `${author.label}${rest.label ? ` ${/^\(/.test(rest.label) ? rest.label : `(${rest.label})`}` : ""}`;
2162
+ const who = () => this.apaNarrativeAnd ? author.label.replace(/,? & /g, (m) => m.startsWith(",") ? ", and " : " and ") : author.label;
2163
+ const join = () => `${who()}${rest.label ? ` ${/^\(/.test(rest.label) ? rest.label : `(${rest.label})`}` : ""}`;
2095
2164
  const listeners = [];
2096
2165
  author.listeners.push(() => listeners.forEach((l) => l(join())));
2097
2166
  rest.listeners.push(() => listeners.forEach((l) => l(join())));
@@ -2570,7 +2639,7 @@ function runs(nodes, c, st = {}) {
2570
2639
  for (const n of nodes) {
2571
2640
  switch (n.type) {
2572
2641
  case "text":
2573
- out.push(tr(n.value));
2642
+ out.push(RTL_RE.test(n.value) ? tr(n.value, { rightToLeft: true }) : tr(n.value));
2574
2643
  break;
2575
2644
  case "strong":
2576
2645
  out.push(...runs(n.children, c, { ...st, bold: true }));
@@ -2722,6 +2791,10 @@ function numberingRef(c, ordered, start) {
2722
2791
  }
2723
2792
  return ref;
2724
2793
  }
2794
+ function bidiPara(nodes) {
2795
+ const text = nodes.map((n) => "value" in n ? n.value : "").join("");
2796
+ return isRtlParagraph(text) ? { bidirectional: true } : {};
2797
+ }
2725
2798
  function blocks(list, c, o = {}) {
2726
2799
  const out = [];
2727
2800
  for (const b of list) out.push(...block(b, c, o));
@@ -2746,6 +2819,7 @@ function block(b, c, o) {
2746
2819
  const kids = [...b.number ? [new d.TextRun({ text: `${b.number} `, color: HEX.muted })] : [], ...runs(b.children, c)];
2747
2820
  return [
2748
2821
  new d.Paragraph({
2822
+ ...bidiPara(b.children),
2749
2823
  heading,
2750
2824
  keepNext: true,
2751
2825
  keepLines: true,
@@ -2755,7 +2829,7 @@ function block(b, c, o) {
2755
2829
  ];
2756
2830
  }
2757
2831
  case "paragraph":
2758
- return [para(c, o, { children: runs(b.children, c), widowControl: true })];
2832
+ return [para(c, o, { children: runs(b.children, c), widowControl: true, ...bidiPara(b.children) })];
2759
2833
  case "list": {
2760
2834
  const level = o.listLevel ?? 0;
2761
2835
  const ref = numberingRef(c, b.ordered, b.start);
@@ -2772,7 +2846,14 @@ function block(b, c, o) {
2772
2846
  })
2773
2847
  );
2774
2848
  } else {
2775
- out.push(new d.Paragraph({ numbering: { reference: ref, level, instance }, children: firstRuns, widowControl: true }));
2849
+ out.push(
2850
+ new d.Paragraph({
2851
+ numbering: { reference: ref, level, instance },
2852
+ children: firstRuns,
2853
+ widowControl: true,
2854
+ ...first?.type === "paragraph" ? bidiPara(first.children) : {}
2855
+ })
2856
+ );
2776
2857
  }
2777
2858
  const tail = first && first.type !== "paragraph" ? [first, ...rest] : rest;
2778
2859
  for (const child of tail) {
@@ -2814,6 +2895,7 @@ function block(b, c, o) {
2814
2895
  borders: { top: none, left: none, right: none, bottom: head ? { ...border, size: 10, color: HEX.heading } : border },
2815
2896
  children: [
2816
2897
  new d.Paragraph({
2898
+ ...bidiPara(cells[i] ?? []),
2817
2899
  alignment: alignOf(i),
2818
2900
  keepNext: keep && !last,
2819
2901
  children: runs(cells[i] ?? [], c, head ? { bold: true } : {})
@@ -3027,7 +3109,11 @@ async function documentToDocx(tree, options = {}) {
3027
3109
  numbering: { config: [...c.numbering.entries()].map(([reference, levels2]) => ({ reference, levels: levels2 })) },
3028
3110
  styles: {
3029
3111
  default: {
3030
- document: { run: { font: "Cambria", size, color: HEX.text }, paragraph: { spacing: { after: 140, line: 300 } } }
3112
+ // East-Asian text gets a CJK face (Word and LibreOffice substitute an installed one).
3113
+ document: {
3114
+ run: { font: { ascii: "Cambria", hAnsi: "Cambria", cs: "Arial", eastAsia: "Yu Gothic" }, size, sizeComplexScript: size, color: HEX.text },
3115
+ paragraph: { spacing: { after: 140, line: 300 } }
3116
+ }
3031
3117
  },
3032
3118
  paragraphStyles: [
3033
3119
  heading("Heading1", "Heading 1", s.fontSize * 1.8, 0),
@@ -3170,7 +3256,7 @@ function renderInlineHtml(nodes, x) {
3170
3256
  case "cite":
3171
3257
  return n.resolved ? `<a class="doc-cite" href="${escapeHtml(href("ref-" + (n.keys[0] ?? ""), x))}">${escapeHtml(n.label)}</a>` : `<span class="doc-cite doc-cite-missing">${escapeHtml(n.label)}</span>`;
3172
3258
  case "math":
3173
- return mathHtml(n.tex, n.display, x);
3259
+ return mathHtml(n.tex, n.display, x, true);
3174
3260
  case "noteRef":
3175
3261
  return `<sup class="doc-note-ref"><a href="${escapeHtml(href(`note-${n.n}`, x))}" id="noteref-${n.n}">${n.n}</a></sup>`;
3176
3262
  default:
@@ -3178,10 +3264,11 @@ function renderInlineHtml(nodes, x) {
3178
3264
  }
3179
3265
  }).join("");
3180
3266
  }
3181
- function mathHtml(tex, display, x) {
3267
+ function mathHtml(tex, display, x, inText = false) {
3182
3268
  const m = x.tree.math.get(mathKey(tex, display));
3183
3269
  if (m?.mathml && !m.error) {
3184
- return display ? `<div class="doc-math-display">${m.mathml}</div>` : `<span class="doc-math">${m.mathml}</span>`;
3270
+ if (!display) return `<span class="doc-math">${m.mathml}</span>`;
3271
+ return inText ? `<span class="doc-math-display">${m.mathml}</span>` : `<div class="doc-math-display">${m.mathml}</div>`;
3185
3272
  }
3186
3273
  return `<code class="doc-math-tex">${escapeHtml(display ? `$$${tex}$$` : `$${tex}$`)}</code>`;
3187
3274
  }
@@ -3212,10 +3299,10 @@ function renderBlockHtml(b, x) {
3212
3299
  switch (b.type) {
3213
3300
  case "heading": {
3214
3301
  const num = b.number ? `<span class="doc-heading-number">${b.number}</span> ` : "";
3215
- return `<h${b.level} id="${escapeHtml(b.id)}">${num}${renderInlineHtml(b.children, x)}</h${b.level}>`;
3302
+ return `<h${b.level} id="${escapeHtml(b.id)}" dir="auto">${num}${renderInlineHtml(b.children, x)}</h${b.level}>`;
3216
3303
  }
3217
3304
  case "paragraph":
3218
- return `<p>${renderInlineHtml(b.children, x)}</p>`;
3305
+ return `<p dir="auto">${renderInlineHtml(b.children, x)}</p>`;
3219
3306
  case "list": {
3220
3307
  const tag = b.ordered ? "ol" : "ul";
3221
3308
  const start = b.ordered && b.start !== 1 ? ` start="${b.start}"` : "";
@@ -3223,12 +3310,12 @@ function renderBlockHtml(b, x) {
3223
3310
  const items = b.items.map((i) => {
3224
3311
  const box = i.checked === null ? "" : `<span class="doc-task-box${i.checked ? " is-checked" : ""}" role="img" aria-label="${i.checked ? "done" : "not done"}"></span>`;
3225
3312
  const inner = i.children.length === 1 && i.children[0]?.type === "paragraph" ? renderInlineHtml(i.children[0].children, x) : renderBlocksHtml(i.children, x);
3226
- return `<li${i.checked !== null ? ' class="doc-task"' : ""}>${box}${inner}</li>`;
3313
+ return `<li dir="auto"${i.checked !== null ? ' class="doc-task"' : ""}>${box}${inner}</li>`;
3227
3314
  }).join("");
3228
3315
  return `<${tag}${start}${task ? ' class="doc-task-list"' : ""}>${items}</${tag}>`;
3229
3316
  }
3230
3317
  case "blockquote":
3231
- return `<blockquote>${renderBlocksHtml(b.children, x)}</blockquote>`;
3318
+ return `<blockquote dir="auto">${renderBlocksHtml(b.children, x)}</blockquote>`;
3232
3319
  case "code":
3233
3320
  return `<pre class="doc-code"${b.lang ? ` data-language="${escapeHtml(b.lang)}"` : ""}><code>${escapeHtml(
3234
3321
  b.value
@@ -3238,8 +3325,8 @@ function renderBlockHtml(b, x) {
3238
3325
  const a = b.align[i];
3239
3326
  return a ? ` style="text-align:${a}"` : "";
3240
3327
  };
3241
- const head = `<thead><tr>${b.header.map((c, i) => `<th${al(i)}>${renderInlineHtml(c, x)}</th>`).join("")}</tr></thead>`;
3242
- const body = `<tbody>${b.rows.map((r) => `<tr>${r.map((c, i) => `<td${al(i)}>${renderInlineHtml(c, x)}</td>`).join("")}</tr>`).join("")}</tbody>`;
3328
+ const head = `<thead><tr>${b.header.map((c, i) => `<th dir="auto"${al(i)}>${renderInlineHtml(c, x)}</th>`).join("")}</tr></thead>`;
3329
+ const body = `<tbody>${b.rows.map((r) => `<tr>${r.map((c, i) => `<td dir="auto"${al(i)}>${renderInlineHtml(c, x)}</td>`).join("")}</tr>`).join("")}</tbody>`;
3243
3330
  const id = b.caption?.id ? ` id="tbl:${escapeHtml(b.caption.id)}"` : "";
3244
3331
  return `<div class="doc-table-wrap"${id}><table>${b.caption ? captionHtml(b.caption, "caption", x) : ""}${head}${body}</table></div>`;
3245
3332
  }
@@ -3343,7 +3430,7 @@ table caption{caption-side:top;text-align:left;margin-bottom:4pt;}
3343
3430
  .doc-figure figcaption{margin-top:4pt;}
3344
3431
  .doc-image-missing{border:1px dashed var(--matrx-print-rule,#d1d5db);padding:16pt;color:var(--matrx-print-muted,#6b7280);}
3345
3432
  .doc-inline-img{max-height:1.2em;vertical-align:middle;}
3346
- .doc-math-display{margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
3433
+ .doc-math-display{display:block;margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
3347
3434
  .doc-math-display math{font-size:1.15em;}
3348
3435
  .doc-math-tex{color:var(--matrx-print-danger,#b91c1c);}
3349
3436
  .doc-task-list{list-style:none;padding-left:0.2em;}
@@ -3422,7 +3509,7 @@ blockquote{margin:0 0 0.8em;padding-left:1em;border-left:3px solid #d1d5db;color
3422
3509
  .doc-cover-logo{max-height:5em;}
3423
3510
  .doc-cover-title{font-size:2em;}
3424
3511
  .matrx-page-break{page-break-after:always;break-after:page;}
3425
- .doc-math-display{margin:0.6em 0;text-align:center;}
3512
+ .doc-math-display{display:block;margin:0.6em 0;text-align:center;}
3426
3513
  `.trim();
3427
3514
  function extOf(mime) {
3428
3515
  return mime === "image/png" ? "png" : mime === "image/gif" ? "gif" : "jpg";
@@ -3709,7 +3796,7 @@ function breakRuns(input, width, t) {
3709
3796
  for (let guard = 0; guard < 50 && atoms.length; guard++) {
3710
3797
  const gw = atoms.reduce((n, a) => n + w(a), 0);
3711
3798
  const sw = pendingSpace && line > 0 ? w(pendingSpace) : 0;
3712
- if (line + sw + gw <= W || line === 0 && atoms.length !== 1) {
3799
+ if (line + sw + gw <= W || line === 0 && (atoms.length !== 1 || gw <= width)) {
3713
3800
  if (sw && pendingSpace) out.push(pendingSpace);
3714
3801
  out.push(...atoms);
3715
3802
  line += sw + gw;
@@ -3833,11 +3920,14 @@ function withFallbackFonts(nodes, c) {
3833
3920
  const fb = fallbackFor(cp);
3834
3921
  if (fb) {
3835
3922
  flush();
3836
- out.push({ ...n, text: ch, font: fb, bold: false, italics: false });
3923
+ const prev = out[out.length - 1];
3924
+ if (prev && typeof prev === "object" && prev.font === fb && prev.__fb) prev.text = `${prev.text}${ch}`;
3925
+ else out.push({ ...n, text: ch, font: fb, bold: false, italics: false, __fb: true });
3837
3926
  } else dropped = true;
3838
3927
  }
3839
3928
  flush();
3840
3929
  }
3930
+ for (const o of out) if (typeof o === "object") delete o.__fb;
3841
3931
  if (dropped && !c.glyphs.announced) {
3842
3932
  c.glyphs.announced = true;
3843
3933
  c.tree.notices.push({
@@ -3864,8 +3954,13 @@ function drawable(text, c) {
3864
3954
  }
3865
3955
  function bodyRuns(nodes, c, width = c.width) {
3866
3956
  const r = runs2(nodes, c);
3867
- return c.typo ? breakRuns(r, width, c.typo) : r;
3957
+ const text = plain(nodes);
3958
+ if (!RTL_RE.test(text)) return c.typo ? breakRuns(r, width, c.typo) : r;
3959
+ const typo = c.typo ?? (c.faces ? { split: (w) => [w], measure: c.faces.measure } : void 0);
3960
+ const lines = typo ? breakRuns(r, width, typo) : r;
3961
+ return visualOrder(lines, isRtlParagraph(text));
3868
3962
  }
3963
+ var alignFor = (nodes) => isRtlParagraph(plain(nodes)) ? { alignment: "right" } : {};
3869
3964
  function runs2(nodes, c, st = {}) {
3870
3965
  const out = [];
3871
3966
  const base = (raw) => {
@@ -4057,6 +4152,7 @@ function estimateLines(text, c, size = c.s.fontSize) {
4057
4152
  function columnWidths(b, n, size, c) {
4058
4153
  const measure = (text, bold) => c.faces ? c.faces.measure(text, { bold, fontSize: size }) : text.length * size * 0.5;
4059
4154
  const pad = 10;
4155
+ const slack = 1;
4060
4156
  const min = new Array(n).fill(0);
4061
4157
  const max = new Array(n).fill(0);
4062
4158
  const rows = [[b.header, true], ...b.rows.map((r) => [r, false])];
@@ -4064,8 +4160,8 @@ function columnWidths(b, n, size, c) {
4064
4160
  for (let i = 0; i < n; i++) {
4065
4161
  const t = plain(r[i] ?? []);
4066
4162
  const longest = t.split(/\s+/).reduce((m, w) => Math.max(m, measure(w, head)), 0);
4067
- min[i] = Math.max(min[i], Math.min(longest, c.width * 0.35) + pad);
4068
- max[i] = Math.max(max[i], measure(t, head) + pad);
4163
+ min[i] = Math.max(min[i], Math.min(longest * slack, c.width * 0.35) + pad);
4164
+ max[i] = Math.max(max[i], measure(t, head) * slack + pad);
4069
4165
  }
4070
4166
  }
4071
4167
  const avail = c.width;
@@ -4129,8 +4225,11 @@ function block2(b, c) {
4129
4225
  switch (b.type) {
4130
4226
  case "heading": {
4131
4227
  const rel = Math.min(6, b.level - c.minLevel + 1);
4228
+ const rtlHeading = RTL_RE.test(b.plain);
4229
+ const headRuns = rtlHeading ? visualOrder(runs2(b.children, c), isRtlParagraph(b.plain)) : runs2(b.children, c);
4132
4230
  const node = {
4133
- text: [...b.number ? [{ text: `${b.number} `, color: COLOR.muted }] : [], ...runs2(b.children, c)],
4231
+ ...rtlHeading ? alignFor(b.children) : {},
4232
+ text: [...b.number ? [{ text: `${b.number} `, color: COLOR.muted }] : [], ...headRuns],
4134
4233
  style: `h${Math.min(6, b.level)}`,
4135
4234
  headlineLevel: b.level,
4136
4235
  id: b.id
@@ -4144,12 +4243,12 @@ function block2(b, c) {
4144
4243
  }
4145
4244
  case "paragraph": {
4146
4245
  const text = bodyRuns(b.children, c);
4147
- const p = { text, style: "p" };
4246
+ const p = { text, style: "p", ...alignFor(b.children) };
4148
4247
  return estimateLines(plain(b.children), c) <= 5 ? { stack: [p], unbreakable: true } : p;
4149
4248
  }
4150
4249
  case "list": {
4151
4250
  const items = b.items.map((it) => {
4152
- const content = it.children.length === 1 && it.children[0]?.type === "paragraph" ? [{ text: bodyRuns(it.children[0].children, c, c.width - 20 - (it.checked !== null ? 8 : 0)), margin: [0, 0, 0, 2] }] : blocks2(it.children, { ...c, width: c.width - 18 });
4251
+ const content = it.children.length === 1 && it.children[0]?.type === "paragraph" ? [{ text: bodyRuns(it.children[0].children, c, c.width - 20 - (it.checked !== null ? 8 : 0)), margin: [0, 0, 0, 2], ...alignFor(it.children[0].children) }] : blocks2(it.children, { ...c, width: c.width - 18 });
4153
4252
  if (it.checked !== null) return { ...taskItem(it.checked, content, c), listType: "none" };
4154
4253
  return content.length === 1 ? content[0] : { stack: content };
4155
4254
  });
@@ -4202,15 +4301,24 @@ function block2(b, c) {
4202
4301
  case "table": {
4203
4302
  const n = Math.max(b.header.length, ...b.rows.map((r) => r.length), 1);
4204
4303
  const size = c.s.fontSize * 0.92;
4304
+ const widths = columnWidths(b, n, size, c);
4305
+ const cellRuns = (inl, i, head) => {
4306
+ const r = runs2(inl, c).map((x) => typeof x === "object" ? { ...x, fontSize: x.fontSize ?? size, ...head ? { bold: true } : {} } : x);
4307
+ const w = (widths[i] ?? c.width) - 10;
4308
+ if (RTL_RE.test(plain(inl))) {
4309
+ const typo = c.typo ?? (c.faces ? { split: (x) => [x], measure: c.faces.measure } : void 0);
4310
+ return visualOrder(typo ? breakRuns(r, w, typo) : r, isRtlParagraph(plain(inl)));
4311
+ }
4312
+ return c.typo && !head ? breakRuns(r, w, c.typo) : r;
4313
+ };
4205
4314
  const cell = (inl, i, head) => ({
4206
- text: runs2(inl ?? [], c),
4315
+ text: cellRuns(inl ?? [], i, head),
4207
4316
  bold: head,
4208
- alignment: b.align[i] ?? "left",
4317
+ alignment: b.align[i] ?? (isRtlParagraph(plain(inl ?? [])) ? "right" : "left"),
4209
4318
  fontSize: size,
4210
4319
  color: head ? COLOR.heading : COLOR.text
4211
4320
  });
4212
4321
  const row = (r, head) => Array.from({ length: n }, (_, i) => cell(r[i], i, head));
4213
- const widths = columnWidths(b, n, size, c);
4214
4322
  const lineH = size * 1.3;
4215
4323
  const pageH = c.pageHeight;
4216
4324
  const rowHeight = (r) => Math.max(...r.map((inl, i) => estimateLines(plain(inl), { ...c, width: (widths[i] ?? c.width) - 10 }, size) * lineH)) + 8;
@@ -4456,12 +4564,16 @@ async function loadTypography(tree, loaders = {}) {
4456
4564
  const base = await loadFaces(vfs, s.fontSize);
4457
4565
  let needEmoji = false;
4458
4566
  let needSymbols = false;
4567
+ let needArabic = false;
4568
+ let needHebrew = false;
4459
4569
  const cjk = /* @__PURE__ */ new Set();
4460
4570
  for (const ch of documentText(tree)) {
4461
4571
  const cp = ch.codePointAt(0);
4462
4572
  if (cp < 32 || cp === 8205 || cp === 65039 || base.body(cp)) continue;
4463
4573
  if (EMOJI_CP.test(ch)) needEmoji = true;
4464
- else if (isCjk(cp)) cjk.add(cp);
4574
+ else if (isCjk(cp) || isHangul(cp)) cjk.add(cp);
4575
+ else if (isArabic(cp)) needArabic = true;
4576
+ else if (isHebrew(cp)) needHebrew = true;
4465
4577
  else needSymbols = true;
4466
4578
  }
4467
4579
  const fallbacks = [];
@@ -4473,6 +4585,14 @@ async function loadTypography(tree, loaders = {}) {
4473
4585
  const b = await registerFace("NotoSymbols", "NotoSansMath.woff", loaders.symbols ?? defaultSymbolFontLoader, tree, "symbols");
4474
4586
  if (b) fallbacks.push({ font: "NotoSymbols", bytes: b });
4475
4587
  }
4588
+ if (needArabic) {
4589
+ const b = await registerFace("NotoArabic", "NotoSansArabic.woff", loaders.arabic ?? urlLoader(NOTO_ARABIC_URL), tree, "Arabic");
4590
+ if (b) fallbacks.push({ font: "NotoArabic", bytes: b });
4591
+ }
4592
+ if (needHebrew) {
4593
+ const b = await registerFace("NotoHebrew", "NotoSansHebrew.woff", loaders.hebrew ?? urlLoader(NOTO_HEBREW_URL), tree, "Hebrew");
4594
+ if (b) fallbacks.push({ font: "NotoHebrew", bytes: b });
4595
+ }
4476
4596
  if (cjk.size) {
4477
4597
  let subsets = [];
4478
4598
  try {
@@ -4488,7 +4608,7 @@ async function loadTypography(tree, loaders = {}) {
4488
4608
  if (!subsets.length) {
4489
4609
  tree.notices.push({
4490
4610
  code: "glyph-substituted",
4491
- message: "The Japanese / Chinese font could not be loaded, so those characters were left out of the PDF (Word, EPUB and HTML keep them).",
4611
+ message: "The Chinese / Japanese / Korean font could not be loaded, so those characters were left out of the PDF (Word, EPUB and HTML keep them).",
4492
4612
  remedy: "Check the internet connection (the font comes from cdn.jsdelivr.net) and export again."
4493
4613
  });
4494
4614
  }
@@ -4516,6 +4636,12 @@ var urlLoader = (url) => async () => {
4516
4636
  var defaultEmojiFontLoader = urlLoader(NOTO_EMOJI_URL);
4517
4637
  var defaultSymbolFontLoader = urlLoader(NOTO_SYMBOLS_URL);
4518
4638
  var NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
4639
+ var NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
4640
+ var NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
4641
+ var NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
4642
+ var isHangul = (cp) => cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687;
4643
+ var isArabic = (cp) => cp >= 1536 && cp <= 1791 || cp >= 1872 && cp <= 1919 || cp >= 2208 && cp <= 2303 || cp >= 64336 && cp <= 65023 || cp >= 65136 && cp <= 65279;
4644
+ var isHebrew = (cp) => cp >= 1424 && cp <= 1535 || cp >= 64285 && cp <= 64335;
4519
4645
  function parseUnicodeRangeCss(css) {
4520
4646
  const out = [];
4521
4647
  for (const block3 of css.split("@font-face").slice(1)) {
@@ -4531,19 +4657,25 @@ function parseUnicodeRangeCss(css) {
4531
4657
  }
4532
4658
  return out;
4533
4659
  }
4534
- var defaultCjkFontLoader = async (cps) => {
4660
+ async function unicodeRangeFaces(base, cps) {
4535
4661
  if (typeof fetch !== "function" || cps.length === 0) return [];
4536
- const res = await fetch(`${NOTO_CJK_BASE}/400.css`);
4662
+ const res = await fetch(`${base}/400.css`);
4537
4663
  if (!res.ok) return [];
4538
4664
  const faces = parseUnicodeRangeCss(await res.text()).filter((f) => cps.some((cp) => f.ranges.some(([a, b]) => cp >= a && cp <= b)));
4539
4665
  const out = [];
4540
4666
  await Promise.all(
4541
4667
  faces.map(async (f) => {
4542
- const r = await fetch(`${NOTO_CJK_BASE}/files/${f.file}.woff`);
4668
+ const r = await fetch(`${base}/files/${f.file}.woff`);
4543
4669
  if (r.ok) out.push({ name: f.file, bytes: new Uint8Array(await r.arrayBuffer()) });
4544
4670
  })
4545
4671
  );
4546
4672
  return out;
4673
+ }
4674
+ var defaultCjkFontLoader = async (cps) => {
4675
+ const kr = cps.filter(isHangul);
4676
+ const jp = cps.filter((cp) => !isHangul(cp));
4677
+ const [a, b] = await Promise.all([unicodeRangeFaces(NOTO_KR_BASE, kr), unicodeRangeFaces(NOTO_CJK_BASE, jp)]);
4678
+ return [...a, ...b];
4547
4679
  };
4548
4680
  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;
4549
4681
  var EMOJI_CP = /\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20E3/u;
@@ -4712,6 +4844,70 @@ function removeThinkingContent(content) {
4712
4844
  return result.trim();
4713
4845
  }
4714
4846
  var PAGE_BREAK_HTML = `<div class="${PAGE_BREAK_CLASS}" role="separator" aria-label="${PAGE_BREAK_LABEL}" data-label="${PAGE_BREAK_LABEL}"></div>`;
4847
+ var CODE_ARTIFACT_LANGUAGE = {
4848
+ html: "html",
4849
+ react: "tsx",
4850
+ svg: "svg",
4851
+ mermaid: "mermaid",
4852
+ diff: "diff",
4853
+ iframe: "html",
4854
+ code: ""
4855
+ };
4856
+ function parseTagAttributes(tag) {
4857
+ const attrs = {};
4858
+ for (const m of tag.matchAll(/([a-zA-Z_][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
4859
+ attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? "";
4860
+ }
4861
+ return attrs;
4862
+ }
4863
+ function unwrapArtifactsToMarkdown(markdown) {
4864
+ if (!/<artifact\b/i.test(markdown)) return markdown;
4865
+ return markdown.split(/(^[ \t]*(?:`{3,}|~{3,})[\s\S]*?^[ \t]*(?:`{3,}|~{3,})[ \t]*$)/m).map(
4866
+ (part, i) => i % 2 === 1 ? part : part.replace(/<artifact\b((?:[^>"']|"[^"]*"|'[^']*')*?)>([\s\S]*?)<\/artifact>/gi, (_m, tag, body) => {
4867
+ const attrs = parseTagAttributes(tag);
4868
+ const type = (attrs.type ?? "").toLowerCase();
4869
+ const trimmed = body.replace(/^\n+|\n+$/g, "");
4870
+ if (!trimmed.trim()) return "";
4871
+ if (/^\s*(`{3,}|~{3,})/.test(trimmed)) return `
4872
+
4873
+ ${trimmed}
4874
+
4875
+ `;
4876
+ if (type in CODE_ARTIFACT_LANGUAGE) {
4877
+ const lang = CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || "";
4878
+ return `
4879
+
4880
+ \`\`\`\`${lang}
4881
+ ${trimmed}
4882
+ \`\`\`\`
4883
+
4884
+ `;
4885
+ }
4886
+ if (looksLikeJson(trimmed)) return `
4887
+
4888
+ \`\`\`\`json
4889
+ ${JSON.stringify(JSON.parse(trimmed), null, 2)}
4890
+ \`\`\`\`
4891
+
4892
+ `;
4893
+ return `
4894
+
4895
+ ${trimmed}
4896
+
4897
+ `;
4898
+ })
4899
+ ).join("");
4900
+ }
4901
+ function looksLikeJson(body) {
4902
+ const t = body.trim();
4903
+ if (!/^[[{]/.test(t)) return false;
4904
+ try {
4905
+ JSON.parse(t);
4906
+ return true;
4907
+ } catch {
4908
+ return false;
4909
+ }
4910
+ }
4715
4911
  var LINK_URL = String.raw`((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?`;
4716
4912
  var IMAGE_RE = new RegExp(String.raw`!\[([^\]]*)\]\(` + LINK_URL + String.raw`\)`, "g");
4717
4913
  var LINK_RE = new RegExp(String.raw`\[([^\]]+)\]\(` + LINK_URL + String.raw`\)`, "g");
@@ -4798,7 +4994,7 @@ function uniqueId(ctx, base) {
4798
4994
  return id;
4799
4995
  }
4800
4996
  var CITE_GROUP_RE = /\[(-?@[^[\]]+)\]/g;
4801
- var BARE_RE = /(^|[^\w@.\\])@((?:fig|tbl|sec|eq):[\w-]+|[A-Za-z0-9_](?:[\w:.#$%&+?<>~/-]*\w)?)/g;
4997
+ var BARE_RE = /(^|[^\w@.\\])@((?:fig|tbl|sec|eq):[\w-]+|[A-Za-z0-9_](?:[\w:.#$%&+?<>~/-]*\w)?)(?: \[([^\[\]@]+)\])?/g;
4802
4998
  function scanText(value, ctx) {
4803
4999
  const out = [];
4804
5000
  const pushText = (s) => {
@@ -4819,8 +5015,8 @@ function scanText(value, ctx) {
4819
5015
  last2 = start + key.length + 1;
4820
5016
  } else if (ctx.refs.has(key)) {
4821
5017
  pushText(s.slice(last2, start));
4822
- out.push(cite([{ key, suppressAuthor: false }], ctx, true));
4823
- last2 = start + key.length + 1;
5018
+ out.push(cite([{ key, locator: m[3]?.trim(), suppressAuthor: false }], ctx, true));
5019
+ last2 = start + key.length + 1 + (m[3] !== void 0 ? m[3].length + 3 : 0);
4824
5020
  }
4825
5021
  }
4826
5022
  pushText(s.slice(last2));
@@ -5166,7 +5362,7 @@ function parseDocument(source, options = {}) {
5166
5362
  const settings = resolveSettings(fm.data, sink, options);
5167
5363
  for (const n of sink) notify(n);
5168
5364
  const body = removeThinkingContent(fm.body);
5169
- const lifted = extractMath(body);
5365
+ const lifted = extractMath(unwrapArtifactsToMarkdown(body));
5170
5366
  const root = fromMarkdown(prepass(lifted.text), {
5171
5367
  extensions: [gfm()],
5172
5368
  mdastExtensions: [gfmFromMarkdown()]
@@ -5561,8 +5757,11 @@ export {
5561
5757
  CitationEngine,
5562
5758
  DEFAULT_FOOTER,
5563
5759
  DOCUMENT_FORMATS,
5760
+ NOTO_ARABIC_URL,
5564
5761
  NOTO_CJK_BASE,
5565
5762
  NOTO_EMOJI_URL,
5763
+ NOTO_HEBREW_URL,
5764
+ NOTO_KR_BASE,
5566
5765
  NOTO_SYMBOLS_URL,
5567
5766
  PAGE_SIZES,
5568
5767
  buildPdfDefinition,