@ai-matrx/print 0.7.0 → 0.8.0

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.
@@ -1,3 +1,50 @@
1
+ type SplitWord = (word: string) => string[];
2
+ /** Width of `text` in points for a pdfmake run's font/size. */
3
+ type Measure = (text: string, run: Record<string, unknown>) => number;
4
+ /** The embedded faces, measured and queried with fontkit (what pdfkit embeds). */
5
+ interface Faces {
6
+ measure: Measure;
7
+ /** Can the body font draw this code point? */
8
+ body: (cp: number) => boolean;
9
+ /** Fallback faces in order (pdfmake font name + coverage). */
10
+ fallbacks: Array<{
11
+ font: string;
12
+ has: (cp: number) => boolean;
13
+ }>;
14
+ }
15
+ interface Typography {
16
+ split: SplitWord;
17
+ measure: Measure;
18
+ }
19
+
20
+ interface MathSpan {
21
+ tex: string;
22
+ display: boolean;
23
+ }
24
+ /** Replace every math span with a private-use token; returns the spans. */
25
+ declare function extractMath(body: string): {
26
+ text: string;
27
+ spans: MathSpan[];
28
+ };
29
+ interface TypesetMath {
30
+ /** MathML (<math …>…</math>) for HTML/EPUB and the Word/PDF-inline writers. */
31
+ mathml: string;
32
+ /** SVG for PDF display math; widths in ex (1ex ≈ 0.442em in MathJax). */
33
+ svg?: string | undefined;
34
+ widthEx?: number | undefined;
35
+ heightEx?: number | undefined;
36
+ /** Error message when the TeX could not be typeset. */
37
+ error?: string | undefined;
38
+ }
39
+ declare const mathKey: (tex: string, display: boolean) => string;
40
+ interface MNode {
41
+ tag: string;
42
+ attrs: Record<string, string>;
43
+ children: MNode[];
44
+ text: string;
45
+ }
46
+ declare function parseMathML(xml: string): MNode;
47
+
1
48
  /**
2
49
  * The document model every output of `@ai-matrx/print/document` is rendered
3
50
  * from. ONE parsed tree → PDF, DOCX, EPUB, HTML — so the four always agree on
@@ -12,7 +59,8 @@ interface Margins {
12
59
  bottom: number;
13
60
  left: number;
14
61
  }
15
- type CitationStyle = "apa" | "mla" | "chicago";
62
+ /** A CSL style id or URL ("apa", "modern-language-association", "nature", https://…). */
63
+ type CitationStyle = string;
16
64
  /**
17
65
  * A running header or footer. Each slot is a template: `{page}`, `{pages}`,
18
66
  * `{title}`, `{subtitle}`, `{author}`, `{date}` are substituted per page.
@@ -85,7 +133,7 @@ interface DocumentSettings {
85
133
  fontSize: number;
86
134
  }
87
135
  interface DocumentNotice {
88
- code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "empty-document";
136
+ code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "content-lost" | "math-error" | "empty-document";
89
137
  message: string;
90
138
  remedy: string;
91
139
  }
@@ -125,6 +173,12 @@ type Inline = {
125
173
  } | {
126
174
  type: "noteRef";
127
175
  n: number;
176
+ }
177
+ /** TeX math; `display` = a `$$…$$` / `\[…\]` formula set inside text. */
178
+ | {
179
+ type: "math";
180
+ tex: string;
181
+ display: boolean;
128
182
  };
129
183
  interface Caption {
130
184
  kind: "fig" | "tbl";
@@ -181,6 +235,11 @@ type Block = {
181
235
  type: "bibliography";
182
236
  } | {
183
237
  type: "notes";
238
+ }
239
+ /** A display formula on its own. */
240
+ | {
241
+ type: "math";
242
+ tex: string;
184
243
  };
185
244
  interface ListItem {
186
245
  checked: boolean | null;
@@ -215,6 +274,11 @@ interface DocTree {
215
274
  children: Block[];
216
275
  }>;
217
276
  notices: DocumentNotice[];
277
+ /**
278
+ * Typeset math keyed by `mathKey(tex, display)`; filled by
279
+ * `prepareDocument` / `exportDocument` (empty after a bare `parseDocument`).
280
+ */
281
+ math: Map<string, TypesetMath>;
218
282
  }
219
283
  /** An image fetched once and shared by every renderer. */
220
284
  interface ResolvedImage {
@@ -226,6 +290,128 @@ interface ResolvedImage {
226
290
  }
227
291
  type ImageMap = Map<string, ResolvedImage | null>;
228
292
 
293
+ /**
294
+ * DocTree → PDF (real text PDF via pdfmake — selectable, searchable,
295
+ * link-bearing; never a screenshot). pdfmake is loaded lazily at call time.
296
+ *
297
+ * Print behaviours: page size / margins / orientation, per-section
298
+ * orientation and 1–3 newspaper columns, cover page, running header/footer
299
+ * with "Page X of Y", different first page, table of contents WITH page
300
+ * numbers, numbered captions, cross-reference links, keep-with-next headings
301
+ * (a heading is never the last thing on a page), short paragraphs never split
302
+ * (orphan/widow control), table rows never split and header rows repeat,
303
+ * tables/code/figures kept whole when they fit on a page.
304
+ */
305
+
306
+ /** Build the pdfmake document definition (exported for tests and hosts). */
307
+ declare function buildPdfDefinition(tree: DocTree, images?: ImageMap, pageCountSink?: (n: number) => void, typo?: Typography, faces?: Faces): Record<string, unknown>;
308
+ /** Loads a fallback font's bytes (TTF/OTF/WOFF); null when unavailable. */
309
+ type FontLoader = () => Promise<Uint8Array | null>;
310
+ /** Noto Emoji (SIL OFL) — the fontsource emoji subset. */
311
+ declare const NOTO_EMOJI_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-emoji@5.3.2/files/noto-emoji-emoji-400-normal.woff";
312
+ /** Noto Sans Math (SIL OFL) — arrows, math operators, box drawing, geometric shapes. */
313
+ declare const NOTO_SYMBOLS_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-math@5.3.0/files/noto-sans-math-latin-400-normal.woff";
314
+ declare const defaultEmojiFontLoader: FontLoader;
315
+ declare const defaultSymbolFontLoader: FontLoader;
316
+ interface FallbackFontLoaders {
317
+ emoji?: FontLoader | undefined;
318
+ symbols?: FontLoader | undefined;
319
+ }
320
+ /**
321
+ * Words pdfmake did not put on a page: a text node with no position was never
322
+ * placed, and a node measured taller than its page (`__height`) whose lines
323
+ * all landed on ONE page was cut off (pdfmake keeps only the first page of an
324
+ * unbreakable block or unbreakable row). Header and footer are functions, not
325
+ * content, so they are not counted.
326
+ */
327
+ declare function countUnplacedWords(content: unknown): number;
328
+ /** Lay out a pdfmake definition (fonts registered); returns the PDF bytes. */
329
+ declare function renderPdfDefinition(def: Record<string, unknown>): Promise<Uint8Array>;
330
+ interface PdfResult {
331
+ bytes: Uint8Array;
332
+ pageCount: number;
333
+ }
334
+ declare function documentToPdf(tree: DocTree, images?: ImageMap, fonts?: FallbackFontLoaders): Promise<PdfResult>;
335
+
336
+ /**
337
+ * Citation SYNTAX (pandoc). Formatting is done by a real CSL processor —
338
+ * citeproc-js over the official CSL styles (`./csl/engine.ts`).
339
+ *
340
+ * Syntax (pandoc): `[@smith2020]`, `[@smith2020, p. 4]`, `[@a; @b]`,
341
+ * `[-@smith2020]` (suppress the author), and a bare `@smith2020` in running
342
+ * text (narrative: "Smith (2020)") — the bare form only fires for a key that
343
+ * is actually in the reference list, so an email or an @-handle is never
344
+ * mistaken for a citation.
345
+ */
346
+ interface CiteRef {
347
+ key: string;
348
+ locator?: string | undefined;
349
+ suppressAuthor: boolean;
350
+ }
351
+ /** Parse the inside of `[...]` into cite refs; null if it is not a citation. */
352
+ declare function parseCitationGroup(inner: string): CiteRef[] | null;
353
+ /**
354
+ * Every citation key a markdown body uses: bracketed groups and bare
355
+ * namespaced keys (`@research:…`, `@file:…`). Used to ask a host resolver
356
+ * for the references the frontmatter does not carry.
357
+ */
358
+ declare function scanCitationKeys(body: string): string[];
359
+
360
+ /**
361
+ * Citations through a real CSL processor — citeproc-js (the engine Zotero and
362
+ * Mendeley use) over styles from the official Citation Style Language
363
+ * repository. Any of its ~10,000 styles works by name:
364
+ *
365
+ * csl: apa | mla | chicago | chicago-notes | ieee | harvard (bundled, offline)
366
+ * csl: nature | vancouver | american-medical-association | … (fetched by name)
367
+ * csl: https://…/my-style.csl (fetched by URL)
368
+ *
369
+ * A dependent style (e.g. `nature` → its parent) is resolved to its
370
+ * independent parent. Note styles (Chicago notes and bibliography, …) turn
371
+ * each citation into a numbered note; in-text styles print it inline.
372
+ */
373
+
374
+ /** Friendly names → CSL repository ids. */
375
+ declare const CSL_STYLE_ALIASES: Readonly<Record<string, string>>;
376
+ /** Normalize a `csl:` value to a repository id (or keep a URL). */
377
+ declare function cslStyleId(name: string): string;
378
+ /** The styles that ship inside the package (offline, no fetch). */
379
+ declare const BUNDLED_STYLE_IDS: readonly string[];
380
+ /** Fetches a URL's text; the host may override (proxy, cache, offline store). */
381
+ type CslFetcher = (url: string) => Promise<string | null>;
382
+ declare const defaultCslFetcher: CslFetcher;
383
+ /** Load a style by id or URL, following a dependent style to its parent. */
384
+ declare function loadCslStyle(name: string, fetcher?: CslFetcher): Promise<string | null>;
385
+ /** Load a CSL locale (en-US is bundled). */
386
+ declare function loadCslLocale(lang: string, fetcher?: CslFetcher): Promise<string | null>;
387
+ /** Locator text ("p. 4", "chap. 3", "§ 2", "12") → CSL label + locator. */
388
+ declare function parseLocator(raw: string | undefined): {
389
+ label?: string | undefined;
390
+ locator?: string | undefined;
391
+ suffix?: string | undefined;
392
+ };
393
+ interface CitationResult {
394
+ /** Plain in-text label, e.g. "(Kim & Alvarez, 2021, p. 58)" or "[1]". */
395
+ label: string;
396
+ /** Called when a LATER citation changes this one (disambiguation, ibid.). */
397
+ onUpdate: (fn: (label: string) => void) => void;
398
+ }
399
+ declare class CitationEngine {
400
+ private readonly engine;
401
+ private readonly clusters;
402
+ /** A note style: every citation becomes a numbered note. */
403
+ readonly isNote: boolean;
404
+ private readonly numeric;
405
+ constructor(styleXml: string, localeXml: string, items: CslItem[], lang: string);
406
+ /** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
407
+ cite(refs: CiteRef[], opts?: {
408
+ narrative?: boolean;
409
+ noteIndex?: number;
410
+ }): CitationResult;
411
+ /** The formatted reference list, in the style's order. */
412
+ bibliography(): BibliographyEntry[];
413
+ }
414
+
229
415
  /**
230
416
  * Document settings carried in the markdown source's YAML frontmatter.
231
417
  *
@@ -297,6 +483,16 @@ declare function fillTemplate(template: string, s: DocumentSettings, page: numbe
297
483
  interface ParseDocumentOptions extends ResolveSettingsOptions {
298
484
  /** Where dropped/degraded content is announced (also collected on the tree). */
299
485
  onNotice?: (notice: DocumentNotice) => void;
486
+ /**
487
+ * CSL inputs loaded ahead of time (`prepareDocument` does this): style XML
488
+ * by style id, and the locale XML for the document's language. Bundled
489
+ * styles (apa, mla, chicago, chicago-notes, ieee, harvard) and en-US need
490
+ * nothing here.
491
+ */
492
+ csl?: {
493
+ styles?: Record<string, string> | undefined;
494
+ locale?: string | undefined;
495
+ } | undefined;
300
496
  }
301
497
  declare function plainText(inlines: Inline[]): string;
302
498
  /** Parse a markdown source (with optional frontmatter) into the document tree. */
@@ -331,27 +527,6 @@ interface DocumentHtmlOptions {
331
527
  /** A complete, standalone print-grade HTML document. */
332
528
  declare function documentToHtml(tree: DocTree, options?: DocumentHtmlOptions): string;
333
529
 
334
- /**
335
- * DocTree → PDF (real text PDF via pdfmake — selectable, searchable,
336
- * link-bearing; never a screenshot). pdfmake is loaded lazily at call time.
337
- *
338
- * Print behaviours: page size / margins / orientation, per-section
339
- * orientation and 1–3 newspaper columns, cover page, running header/footer
340
- * with "Page X of Y", different first page, table of contents WITH page
341
- * numbers, numbered captions, cross-reference links, keep-with-next headings
342
- * (a heading is never the last thing on a page), short paragraphs never split
343
- * (orphan/widow control), table rows never split and header rows repeat,
344
- * tables/code/figures kept whole when they fit on a page.
345
- */
346
-
347
- /** Build the pdfmake document definition (exported for tests and hosts). */
348
- declare function buildPdfDefinition(tree: DocTree, images?: ImageMap, pageCountSink?: (n: number) => void): Record<string, unknown>;
349
- interface PdfResult {
350
- bytes: Uint8Array;
351
- pageCount: number;
352
- }
353
- declare function documentToPdf(tree: DocTree, images?: ImageMap): Promise<PdfResult>;
354
-
355
530
  /**
356
531
  * DocTree → DOCX (Word). Uses the `docx` package, loaded lazily.
357
532
  *
@@ -391,36 +566,6 @@ interface EpubOptions {
391
566
  }
392
567
  declare function documentToEpub(tree: DocTree, options?: EpubOptions): Promise<Uint8Array>;
393
568
 
394
- /**
395
- * Citations and the reference list — pandoc citation syntax over CSL-JSON
396
- * reference items, formatted in three built-in styles:
397
- *
398
- * apa APA 7th edition (author–date) (Smith & Jones, 2020, p. 4)
399
- * mla MLA 9th edition (author–page) (Smith and Jones 4)
400
- * chicago Chicago 17th, author–date (Smith and Jones 2020, 4)
401
- *
402
- * Syntax (pandoc): `[@smith2020]`, `[@smith2020, p. 4]`, `[@a; @b]`,
403
- * `[-@smith2020]` (suppress the author), and a bare `@smith2020` in running
404
- * text (narrative: "Smith (2020)") — the bare form only fires for a key that
405
- * is actually in the reference list, so an email or an @-handle is never
406
- * mistaken for a citation.
407
- */
408
-
409
- interface CiteRef {
410
- key: string;
411
- locator?: string | undefined;
412
- suppressAuthor: boolean;
413
- }
414
- /** Parse the inside of `[...]` into cite refs; null if it is not a citation. */
415
- declare function parseCitationGroup(inner: string): CiteRef[] | null;
416
- /** Format one citation group as in-text text, e.g. "(Smith, 2020, p. 4; Lee, 2019)". */
417
- declare function formatCitation(refs: CiteRef[], lookup: (key: string) => CslItem | undefined, style: CitationStyle, narrative?: boolean): {
418
- label: string;
419
- resolved: boolean;
420
- };
421
- /** Format the reference list for the cited keys, sorted as the style requires. */
422
- declare function formatBibliography(items: CslItem[], style: CitationStyle): BibliographyEntry[];
423
-
424
569
  /**
425
570
  * @ai-matrx/print/document — print-grade documents from markdown.
426
571
  *
@@ -467,10 +612,35 @@ type ImageLoader = (url: string) => Promise<Uint8Array | null>;
467
612
  declare const defaultImageLoader: ImageLoader;
468
613
  /** Resolve every figure/logo image once. Failures are announced, never fatal. */
469
614
  declare function resolveDocumentImages(tree: DocTree, loader?: ImageLoader, onNotice?: (n: DocumentNotice) => void): Promise<ImageMap>;
470
- interface ExportDocumentOptions extends ParseDocumentOptions {
615
+ interface PrepareDocumentOptions extends ParseDocumentOptions {
616
+ /**
617
+ * The host's reference resolver: given citation keys the frontmatter does
618
+ * not define (e.g. `research:<id>`, `file:<id>`), return CSL-JSON items for
619
+ * the ones it knows. AI Matrx resolves its research sources and files.
620
+ */
621
+ resolveReferences?: ((keys: string[]) => Promise<CslItem[]>) | undefined;
622
+ /** Fetches CSL styles/locales not bundled (default: the CSL repositories on GitHub). */
623
+ cslFetcher?: CslFetcher | undefined;
624
+ }
625
+ /**
626
+ * Parse with everything loaded: a citation style that is not bundled is
627
+ * fetched by name (following a dependent style to its parent), the CSL locale
628
+ * for `lang`, and references the host resolves. Use this (or exportDocument)
629
+ * whenever the document may cite; `parseDocument` stays synchronous.
630
+ */
631
+ declare function prepareDocument(source: string, options?: PrepareDocumentOptions): Promise<DocTree>;
632
+ /** Typeset the tree's math (MathJax, loaded only when there is math). */
633
+ declare function typesetDocumentMath(tree: DocTree, onNotice?: (n: DocumentNotice) => void): Promise<void>;
634
+ interface ExportDocumentOptions extends PrepareDocumentOptions {
471
635
  /** File name without extension (default: the document title). */
472
636
  fileName?: string | undefined;
473
637
  imageLoader?: ImageLoader | undefined;
638
+ /**
639
+ * PDF fallback fonts, loaded only when the document has characters Roboto
640
+ * cannot draw: `emoji` (default Noto Emoji) and `symbols` (default Noto Sans
641
+ * Math — arrows, math, box drawing), both from jsDelivr at call time.
642
+ */
643
+ fonts?: FallbackFontLoaders | undefined;
474
644
  }
475
645
  interface DocumentExport {
476
646
  format: DocumentFormat;
@@ -487,4 +657,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
487
657
  /** Browser helper: save an export through a download link. */
488
658
  declare function downloadDocumentExport(exp: DocumentExport): void;
489
659
 
490
- export { type BibliographyEntry, type Block, type Caption, type CitationStyle, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, buildPdfDefinition, defaultImageLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, fillTemplate, formatBibliography, formatCitation, normalizeReference, parseCitationGroup, parseDocument, parseLength, plainText, renderBlocksHtml, resolveDocumentImages, resolveSettings, splitFrontmatter };
660
+ export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, 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_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, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };