@illusions-lab/mdi 2.0.7 → 2.0.9

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/dist/index.d.ts CHANGED
@@ -1,3 +1,68 @@
1
+ import { EpubExportOptions, EpubCover } from '@illusions-lab/mdi-to-epub';
2
+ export { EpubCover, EpubExportOptions } from '@illusions-lab/mdi-to-epub';
3
+ import { ExportProfile } from '@illusions-lab/mdi-export-profile';
4
+ export { ExportProfile } from '@illusions-lab/mdi-export-profile';
5
+ import { Root } from 'mdast';
6
+
7
+ /**
8
+ * Options for a configured EPUB export. The profile controls metadata,
9
+ * chapter splitting and typography; `cover` supplies the actual image data.
10
+ */
11
+ type MdiEpubExportOptions = EpubExportOptions & {
12
+ /** Ergonomic aliases merged into `profile.metadata`. */
13
+ title?: string;
14
+ author?: string;
15
+ publisher?: string;
16
+ identifier?: string;
17
+ language?: string;
18
+ date?: string;
19
+ /** Ergonomic aliases merged into `profile.typesetting` and `profile.epub`. */
20
+ verticalWriting?: boolean;
21
+ fontFamily?: string;
22
+ /** Body type size in typographic points. */
23
+ fontSize?: number;
24
+ /** Baseline multiplier, for example 1.5 for one-and-a-half spacing. */
25
+ lineSpacing?: number;
26
+ textIndent?: number;
27
+ /** Use an ideographic-space-compatible first-line indent. */
28
+ fullwidthSpaceIndent?: boolean;
29
+ /** EPUB has no fixed pages; use `typographic` when overriding type metrics. */
30
+ gridMode?: "strict" | "typographic";
31
+ chapterSplitLevel?: "h1" | "h2" | "h3" | "none";
32
+ /** Shorthand for `cover: { data: coverImage, mediaType: coverMediaType }`. */
33
+ coverImage?: Uint8Array;
34
+ coverMediaType?: EpubCover["mediaType"];
35
+ };
36
+ /**
37
+ * Profile for a configured DOCX export, including page geometry and numbering.
38
+ */
39
+ type MdiDocxExportProfile = ExportProfile & {
40
+ title?: string;
41
+ author?: string;
42
+ publisher?: string;
43
+ identifier?: string;
44
+ language?: string;
45
+ date?: string;
46
+ verticalWriting?: boolean;
47
+ fontFamily?: string;
48
+ /** Point size; requires a publication adapter that supports it. */
49
+ fontSize?: number;
50
+ /** Line-height multiplier; requires a publication adapter that supports it. */
51
+ lineSpacing?: number;
52
+ textIndent?: number;
53
+ pageSize?: NonNullable<ExportProfile["pagination"]>["pageSize"];
54
+ landscape?: boolean;
55
+ /** `strict` enforces the character/line grid; `typographic` prioritizes explicit type metrics. */
56
+ gridMode?: "strict" | "typographic";
57
+ charactersPerLine?: number;
58
+ linesPerPage?: number;
59
+ margins?: NonNullable<NonNullable<ExportProfile["pagination"]>["margins"]>;
60
+ /** Emit literal ideographic spaces for first-line indentation in DOCX. */
61
+ fullwidthSpaceIndent?: boolean;
62
+ showPageNumbers?: boolean;
63
+ pageNumberPosition?: NonNullable<NonNullable<ExportProfile["pagination"]>["pageNumbers"]>["position"];
64
+ pageNumberFormat?: NonNullable<NonNullable<ExportProfile["pagination"]>["pageNumbers"]>["format"];
65
+ };
1
66
  /** MDI specification version implemented by this binding. */
2
67
  declare const MDI_SPEC_VERSION: "2.0";
3
68
  /** Version of the complete Rust-owned document IR. */
@@ -48,6 +113,27 @@ interface MdiDocument {
48
113
  frontmatter?: MdiFrontmatter;
49
114
  children: MdiNode[];
50
115
  }
116
+ /** A source-backed heading available to host navigation and chapter UIs. */
117
+ interface MdiHeading {
118
+ depth: 1 | 2 | 3 | 4 | 5 | 6;
119
+ /** Plain, source-order text. Inline MDI is represented by `node` as well. */
120
+ text: string;
121
+ span?: MdiSourceSpan;
122
+ /** The complete Rust-owned heading node, including inline children. */
123
+ node: MdiNode;
124
+ }
125
+ /** HTML output controls that do not alter MDI semantics. */
126
+ interface MdiHtmlRenderOptions {
127
+ /** Return the semantic contents of `<body>` rather than a standalone page. */
128
+ bodyOnly?: boolean;
129
+ }
130
+ /** A parsed document retained with renderer output for UI diagnostics. */
131
+ interface MdiRenderResult<T> {
132
+ output: T;
133
+ document: MdiDocument;
134
+ diagnostics: MdiDiagnostic[];
135
+ headings: MdiHeading[];
136
+ }
51
137
  /**
52
138
  * Versioned result returned by the Rust parser.
53
139
  *
@@ -68,20 +154,79 @@ type MdiSyntaxDocument = MdiDocument;
68
154
  * language-neutral document IR. JavaScript performs no grammar work.
69
155
  */
70
156
  declare function parse(source: string): MdiSyntaxParseResult;
71
- /** Render complete `.mdi` source to standalone HTML in Rust. */
72
- declare function renderHtml(source: string): string;
157
+ /** Render complete `.mdi` source to standalone semantic HTML in Rust. */
158
+ declare function renderHtml(source: string, options?: MdiHtmlRenderOptions): string;
159
+ /**
160
+ * Render HTML while retaining the Rust parser result that made the semantic
161
+ * decisions. Use this in editors: warnings and source spans remain available
162
+ * instead of being silently discarded, and `headings` can drive navigation or
163
+ * chapter controls without scraping generated HTML.
164
+ *
165
+ * The low-level Rust ABI currently accepts source for rendering, rather than a
166
+ * serializable IR handle. This helper therefore validates with the same Rust
167
+ * parser immediately before rendering and returns that exact parse result.
168
+ */
169
+ declare function renderHtmlWithDiagnostics(source: string, options?: MdiHtmlRenderOptions): MdiRenderResult<string>;
170
+ /**
171
+ * Parse once for a host workflow that needs stable diagnostics and source
172
+ * spans before selecting one of the renderer APIs. The returned document is
173
+ * Rust-owned IR and must not be mutated as an input to a renderer.
174
+ */
175
+ declare function prepareRender(source: string): MdiSyntaxParseResult;
73
176
  /** Build a baseline EPUB 3 archive from complete source in Rust. */
74
177
  declare function renderEpub(source: string): Uint8Array;
178
+ /**
179
+ * Build a profile-configured EPUB 3 archive. This overload is asynchronous
180
+ * because archive generation is performed by the Node.js publication adapter.
181
+ */
182
+ declare function renderEpub(source: string, options: MdiEpubExportOptions): Promise<Uint8Array>;
183
+ /** Build a baseline Rust EPUB while retaining diagnostics for an export UI. */
184
+ declare function renderEpubWithDiagnostics(source: string): MdiRenderResult<Uint8Array>;
185
+ declare function renderEpubWithDiagnostics(source: string, options: MdiEpubExportOptions): Promise<MdiRenderResult<Uint8Array>>;
75
186
  /** Build a baseline DOCX archive from complete source in Rust. */
76
187
  declare function renderDocx(source: string): Uint8Array;
188
+ /**
189
+ * Build a profile-configured DOCX archive. This overload is asynchronous
190
+ * because archive generation is performed by the Node.js publication adapter.
191
+ */
192
+ declare function renderDocx(source: string, profile: MdiDocxExportProfile): Promise<Uint8Array>;
193
+ /** Build a baseline Rust DOCX while retaining diagnostics for an export UI. */
194
+ declare function renderDocxWithDiagnostics(source: string): MdiRenderResult<Uint8Array>;
195
+ declare function renderDocxWithDiagnostics(source: string, profile: MdiDocxExportProfile): Promise<MdiRenderResult<Uint8Array>>;
196
+ /**
197
+ * Build a configured EPUB using the same profile schema as the dedicated
198
+ * publication adapters. Unlike the one-argument {@link renderEpub}, this
199
+ * returns a Promise and supports cover art, metadata, chapters and vertical
200
+ * writing.
201
+ */
202
+ declare function renderEpubWithProfile(source: string, options?: MdiEpubExportOptions): Promise<Uint8Array>;
203
+ /**
204
+ * Build a configured DOCX using the shared print profile. The profile
205
+ * supports metadata, writing mode, paper size, margins and page numbers.
206
+ */
207
+ declare function renderDocxWithProfile(source: string, profile?: MdiDocxExportProfile): Promise<Uint8Array>;
208
+ /**
209
+ * Map the Rust-owned IR to mdast for the publication adapters. This is a
210
+ * structural conversion only: all grammar decisions have already been made
211
+ * by {@link parse}, so it never reparses MDI source in JavaScript.
212
+ */
213
+ /**
214
+ * Convert a Rust-owned MDI document IR into mdast for publication adapters.
215
+ * This performs no parsing and preserves the adapter node conventions.
216
+ */
217
+ declare function toPublicationMdast(document: MdiDocument): Root;
77
218
  /** Parse and normalize complete `.mdi` source with Rust's serializer. */
78
219
  declare function serializeMdi(source: string): string;
79
220
  /** Render complete `.mdi` source to deterministic plain text in Rust. */
80
221
  declare function renderText(source: string): string;
222
+ /** Render plain text without discarding Rust diagnostics and source spans. */
223
+ declare function renderTextWithDiagnostics(source: string): MdiRenderResult<string>;
81
224
  type MdiTextFormat = "txt" | "txt-ruby" | "narou" | "kakuyomu" | "aozora";
82
225
  /** Render a named publication-text convention through Rust. */
83
226
  declare function renderTextFormat(source: string, format: MdiTextFormat, indentPrefix?: string): string;
227
+ /** Render a named text format without discarding Rust diagnostics and spans. */
228
+ declare function renderTextFormatWithDiagnostics(source: string, format: MdiTextFormat, indentPrefix?: string): MdiRenderResult<string>;
84
229
  /** @deprecated Use {@link parse}; it now parses the complete document. */
85
230
  declare const parseMdiSyntax: typeof parse;
86
231
 
87
- export { MDI_IR_VERSION, MDI_SPEC_VERSION, type MdiDiagnostic, type MdiDocument, type MdiFrontmatter, type MdiNode, type MdiParserCapabilities, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextFormat, parse, parseMdiSyntax, renderDocx, renderEpub, renderHtml, renderText, renderTextFormat, serializeMdi };
232
+ export { MDI_IR_VERSION, MDI_SPEC_VERSION, type MdiDiagnostic, type MdiDocument, type MdiDocxExportProfile, type MdiEpubExportOptions, type MdiFrontmatter, type MdiHeading, type MdiHtmlRenderOptions, type MdiNode, type MdiParserCapabilities, type MdiRenderResult, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextFormat, parse, parseMdiSyntax, prepareRender, renderDocx, renderDocxWithDiagnostics, renderDocxWithProfile, renderEpub, renderEpubWithDiagnostics, renderEpubWithProfile, renderHtml, renderHtmlWithDiagnostics, renderText, renderTextFormat, renderTextFormatWithDiagnostics, renderTextWithDiagnostics, serializeMdi, toPublicationMdast };
package/dist/index.js CHANGED
@@ -1,59 +1,42 @@
1
- // src/index.ts
2
1
  import {
3
- parseMdiSyntaxJson,
4
- renderHtml as renderHtmlFromRust,
5
- renderEpub as renderEpubFromRust,
6
- renderDocx as renderDocxFromRust,
7
- renderText as renderTextFromRust,
8
- renderTextFormat as renderTextFormatFromRust,
9
- serializeMdi as serializeMdiFromRust
10
- } from "@illusions-lab/mdi-core";
11
- var MDI_SPEC_VERSION = "2.0";
12
- var MDI_IR_VERSION = "1.0";
13
- function parse(source) {
14
- if (typeof source !== "string") throw new TypeError("source must be a string");
15
- const result = JSON.parse(parseMdiSyntaxJson(source));
16
- if (result.irVersion !== MDI_IR_VERSION) {
17
- throw new Error(`Unsupported MDI IR version: ${String(result.irVersion)}`);
18
- }
19
- return result;
20
- }
21
- function renderHtml(source) {
22
- if (typeof source !== "string") throw new TypeError("source must be a string");
23
- return renderHtmlFromRust(source);
24
- }
25
- function renderEpub(source) {
26
- if (typeof source !== "string") throw new TypeError("source must be a string");
27
- return renderEpubFromRust(source);
28
- }
29
- function renderDocx(source) {
30
- if (typeof source !== "string") throw new TypeError("source must be a string");
31
- return renderDocxFromRust(source);
32
- }
33
- function serializeMdi(source) {
34
- if (typeof source !== "string") throw new TypeError("source must be a string");
35
- return serializeMdiFromRust(source);
36
- }
37
- function renderText(source) {
38
- if (typeof source !== "string") throw new TypeError("source must be a string");
39
- return renderTextFromRust(source);
40
- }
41
- function renderTextFormat(source, format, indentPrefix = "") {
42
- if (typeof source !== "string" || typeof indentPrefix !== "string") {
43
- throw new TypeError("source and indentPrefix must be strings");
44
- }
45
- return renderTextFormatFromRust(source, format, indentPrefix);
46
- }
47
- var parseMdiSyntax = parse;
2
+ MDI_IR_VERSION,
3
+ MDI_SPEC_VERSION,
4
+ parse,
5
+ parseMdiSyntax,
6
+ prepareRender,
7
+ renderDocx,
8
+ renderDocxWithDiagnostics,
9
+ renderDocxWithProfile,
10
+ renderEpub,
11
+ renderEpubWithDiagnostics,
12
+ renderEpubWithProfile,
13
+ renderHtml,
14
+ renderHtmlWithDiagnostics,
15
+ renderText,
16
+ renderTextFormat,
17
+ renderTextFormatWithDiagnostics,
18
+ renderTextWithDiagnostics,
19
+ serializeMdi,
20
+ toPublicationMdast
21
+ } from "./chunk-FA5R33XH.js";
48
22
  export {
49
23
  MDI_IR_VERSION,
50
24
  MDI_SPEC_VERSION,
51
25
  parse,
52
26
  parseMdiSyntax,
27
+ prepareRender,
53
28
  renderDocx,
29
+ renderDocxWithDiagnostics,
30
+ renderDocxWithProfile,
54
31
  renderEpub,
32
+ renderEpubWithDiagnostics,
33
+ renderEpubWithProfile,
55
34
  renderHtml,
35
+ renderHtmlWithDiagnostics,
56
36
  renderText,
57
37
  renderTextFormat,
58
- serializeMdi
38
+ renderTextFormatWithDiagnostics,
39
+ renderTextWithDiagnostics,
40
+ serializeMdi,
41
+ toPublicationMdast
59
42
  };
package/dist/node.cjs ADDED
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/node.ts
21
+ var node_exports = {};
22
+ __export(node_exports, {
23
+ preparePdfExport: () => preparePdfExport,
24
+ preparePdfExportWithDiagnostics: () => preparePdfExportWithDiagnostics,
25
+ renderPdfWithChromium: () => renderPdfWithChromium,
26
+ renderPdfWithChromiumWithDiagnostics: () => renderPdfWithChromiumWithDiagnostics
27
+ });
28
+ module.exports = __toCommonJS(node_exports);
29
+
30
+ // src/index.ts
31
+ var import_mdi_core = require("@illusions-lab/mdi-core");
32
+ var import_mdi_export_profile = require("@illusions-lab/mdi-export-profile");
33
+ var import_yaml = require("yaml");
34
+ var MDI_IR_VERSION = "1.0";
35
+ function parse(source) {
36
+ if (typeof source !== "string") throw new TypeError("source must be a string");
37
+ const result = JSON.parse((0, import_mdi_core.parseMdiSyntaxJson)(source));
38
+ if (result.irVersion !== MDI_IR_VERSION) {
39
+ throw new Error(`Unsupported MDI IR version: ${String(result.irVersion)}`);
40
+ }
41
+ return result;
42
+ }
43
+ function renderHtml(source, options) {
44
+ assertSource(source);
45
+ assertHtmlOptions(options);
46
+ const html = (0, import_mdi_core.renderHtml)(source);
47
+ return options?.bodyOnly ? htmlBody(html) : html;
48
+ }
49
+ function assertSource(source) {
50
+ if (typeof source !== "string") throw new TypeError("source must be a string");
51
+ }
52
+ function assertHtmlOptions(options) {
53
+ if (options === void 0) return;
54
+ assertPlainObject(options, "options");
55
+ if (options.bodyOnly !== void 0 && typeof options.bodyOnly !== "boolean") {
56
+ throw new TypeError("options.bodyOnly must be a boolean");
57
+ }
58
+ }
59
+ function htmlBody(html) {
60
+ const match = /<body(?:\s[^>]*)?>([\s\S]*)<\/body>/i.exec(html);
61
+ if (!match) throw new Error("Rust HTML renderer returned a document without a body");
62
+ return match[1];
63
+ }
64
+ function assertPlainObject(value, label) {
65
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
66
+ throw new TypeError(`${label} must be an object`);
67
+ }
68
+ }
69
+
70
+ // src/node.ts
71
+ var import_mdi_export_profile2 = require("@illusions-lab/mdi-export-profile");
72
+ function preparePdfExport(source, profile) {
73
+ if (typeof source !== "string") throw new TypeError("source must be a string");
74
+ if (profile !== void 0) (0, import_mdi_export_profile2.requireLayoutSystem)(profile);
75
+ const writingMode = parse(source).document.frontmatter?.entries.find(
76
+ (entry) => entry.key === "writing-mode" || entry.key === "writingMode"
77
+ )?.value;
78
+ return {
79
+ html: renderHtml(source),
80
+ profile,
81
+ sourceWritingMode: writingMode === "vertical" || writingMode === "horizontal" ? writingMode : void 0
82
+ };
83
+ }
84
+ function preparePdfExportWithDiagnostics(source, profile) {
85
+ if (typeof source !== "string") throw new TypeError("source must be a string");
86
+ if (profile !== void 0) (0, import_mdi_export_profile2.requireLayoutSystem)(profile);
87
+ const parsed = parse(source);
88
+ const writingMode = parsed.document.frontmatter?.entries.find(
89
+ (entry) => entry.key === "writing-mode" || entry.key === "writingMode"
90
+ )?.value;
91
+ return {
92
+ output: {
93
+ html: renderHtml(source),
94
+ profile,
95
+ sourceWritingMode: writingMode === "vertical" || writingMode === "horizontal" ? writingMode : void 0
96
+ },
97
+ document: parsed.document,
98
+ diagnostics: parsed.diagnostics,
99
+ headings: headingsFromDocument(parsed.document)
100
+ };
101
+ }
102
+ async function renderPdfWithChromium(source, profile, adapter) {
103
+ const request = preparePdfExport(source, profile);
104
+ const chromiumAdapter = adapter ?? await loadPlaywrightPdfAdapter();
105
+ return chromiumAdapter.renderHtmlToPdf(
106
+ request.html,
107
+ request.profile,
108
+ request.sourceWritingMode
109
+ );
110
+ }
111
+ async function renderPdfWithChromiumWithDiagnostics(source, profile, adapter) {
112
+ const prepared = preparePdfExportWithDiagnostics(source, profile);
113
+ const chromiumAdapter = adapter ?? await loadPlaywrightPdfAdapter();
114
+ return {
115
+ ...prepared,
116
+ output: await chromiumAdapter.renderHtmlToPdf(
117
+ prepared.output.html,
118
+ prepared.output.profile,
119
+ prepared.output.sourceWritingMode
120
+ )
121
+ };
122
+ }
123
+ function headingsFromDocument(document) {
124
+ const headings = [];
125
+ const visit = (nodes) => {
126
+ for (const node of nodes) {
127
+ if (node.type === "heading" && isHeadingDepth(node.depth)) {
128
+ headings.push({ depth: node.depth, text: plainText(node), span: node.span, node });
129
+ }
130
+ if (node.children) visit(node.children);
131
+ }
132
+ };
133
+ visit(document.children);
134
+ return headings;
135
+ }
136
+ function isHeadingDepth(value) {
137
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 6;
138
+ }
139
+ function plainText(node) {
140
+ if (node.type === "ruby" && typeof node.base === "string") return node.base;
141
+ return (typeof node.value === "string" ? node.value : "") + (node.children?.map(plainText).join("") ?? "");
142
+ }
143
+ async function loadPlaywrightPdfAdapter() {
144
+ const packageName = "@illusions-lab/mdi-to-pdf";
145
+ try {
146
+ return await import(packageName);
147
+ } catch (error) {
148
+ throw new Error(
149
+ "PDF export needs @illusions-lab/mdi-to-pdf, or pass an Electron-compatible adapter to renderPdfWithChromium().",
150
+ { cause: error }
151
+ );
152
+ }
153
+ }
154
+ // Annotate the CommonJS export names for ESM import in node:
155
+ 0 && (module.exports = {
156
+ preparePdfExport,
157
+ preparePdfExportWithDiagnostics,
158
+ renderPdfWithChromium,
159
+ renderPdfWithChromiumWithDiagnostics
160
+ });
@@ -0,0 +1,41 @@
1
+ import { MdiRenderResult } from './index.cjs';
2
+ import { ExportProfile } from '@illusions-lab/mdi-export-profile';
3
+ export { ExportProfile } from '@illusions-lab/mdi-export-profile';
4
+ import '@illusions-lab/mdi-to-epub';
5
+ import 'mdast';
6
+
7
+ /** A Chromium-capable adapter which turns complete HTML into a PDF. */
8
+ interface MdiPdfChromiumAdapter {
9
+ renderHtmlToPdf(html: string, profile?: ExportProfile, sourceWritingMode?: unknown): Promise<Uint8Array>;
10
+ }
11
+ /**
12
+ * The explicit hand-off from Rust MDI rendering to a browser print engine.
13
+ * Page geometry, fonts, and pagination are browser-layout concerns, so the
14
+ * profile is kept separate from the MDI document IR.
15
+ */
16
+ interface MdiPdfExportRequest {
17
+ html: string;
18
+ profile?: ExportProfile;
19
+ sourceWritingMode?: "vertical" | "horizontal";
20
+ }
21
+ /**
22
+ * Render MDI to Rust-owned HTML and prepare a Chromium PDF request.
23
+ *
24
+ * This browser-safe helper does not load Playwright or launch Chromium.
25
+ * Electron callers may print the returned HTML with their own BrowserWindow.
26
+ */
27
+ declare function preparePdfExport(source: string, profile?: ExportProfile): MdiPdfExportRequest;
28
+ /**
29
+ * Prepare a PDF export without losing the Rust diagnostics, source spans, or
30
+ * source-order headings that a host UI needs to present before printing.
31
+ */
32
+ declare function preparePdfExportWithDiagnostics(source: string, profile?: ExportProfile): MdiRenderResult<MdiPdfExportRequest>;
33
+ /**
34
+ * Print MDI through Chromium. The default Playwright adapter is loaded only
35
+ * by this Node-only entry point; Electron can supply its own adapter.
36
+ */
37
+ declare function renderPdfWithChromium(source: string, profile?: ExportProfile, adapter?: MdiPdfChromiumAdapter): Promise<Uint8Array>;
38
+ /** Print through Chromium while retaining the Rust parse result for host UIs. */
39
+ declare function renderPdfWithChromiumWithDiagnostics(source: string, profile?: ExportProfile, adapter?: MdiPdfChromiumAdapter): Promise<MdiRenderResult<Uint8Array>>;
40
+
41
+ export { type MdiPdfChromiumAdapter, type MdiPdfExportRequest, preparePdfExport, preparePdfExportWithDiagnostics, renderPdfWithChromium, renderPdfWithChromiumWithDiagnostics };
package/dist/node.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { MdiRenderResult } from './index.js';
2
+ import { ExportProfile } from '@illusions-lab/mdi-export-profile';
3
+ export { ExportProfile } from '@illusions-lab/mdi-export-profile';
4
+ import '@illusions-lab/mdi-to-epub';
5
+ import 'mdast';
6
+
7
+ /** A Chromium-capable adapter which turns complete HTML into a PDF. */
8
+ interface MdiPdfChromiumAdapter {
9
+ renderHtmlToPdf(html: string, profile?: ExportProfile, sourceWritingMode?: unknown): Promise<Uint8Array>;
10
+ }
11
+ /**
12
+ * The explicit hand-off from Rust MDI rendering to a browser print engine.
13
+ * Page geometry, fonts, and pagination are browser-layout concerns, so the
14
+ * profile is kept separate from the MDI document IR.
15
+ */
16
+ interface MdiPdfExportRequest {
17
+ html: string;
18
+ profile?: ExportProfile;
19
+ sourceWritingMode?: "vertical" | "horizontal";
20
+ }
21
+ /**
22
+ * Render MDI to Rust-owned HTML and prepare a Chromium PDF request.
23
+ *
24
+ * This browser-safe helper does not load Playwright or launch Chromium.
25
+ * Electron callers may print the returned HTML with their own BrowserWindow.
26
+ */
27
+ declare function preparePdfExport(source: string, profile?: ExportProfile): MdiPdfExportRequest;
28
+ /**
29
+ * Prepare a PDF export without losing the Rust diagnostics, source spans, or
30
+ * source-order headings that a host UI needs to present before printing.
31
+ */
32
+ declare function preparePdfExportWithDiagnostics(source: string, profile?: ExportProfile): MdiRenderResult<MdiPdfExportRequest>;
33
+ /**
34
+ * Print MDI through Chromium. The default Playwright adapter is loaded only
35
+ * by this Node-only entry point; Electron can supply its own adapter.
36
+ */
37
+ declare function renderPdfWithChromium(source: string, profile?: ExportProfile, adapter?: MdiPdfChromiumAdapter): Promise<Uint8Array>;
38
+ /** Print through Chromium while retaining the Rust parse result for host UIs. */
39
+ declare function renderPdfWithChromiumWithDiagnostics(source: string, profile?: ExportProfile, adapter?: MdiPdfChromiumAdapter): Promise<MdiRenderResult<Uint8Array>>;
40
+
41
+ export { type MdiPdfChromiumAdapter, type MdiPdfExportRequest, preparePdfExport, preparePdfExportWithDiagnostics, renderPdfWithChromium, renderPdfWithChromiumWithDiagnostics };
package/dist/node.js ADDED
@@ -0,0 +1,97 @@
1
+ import {
2
+ parse,
3
+ renderHtml
4
+ } from "./chunk-FA5R33XH.js";
5
+
6
+ // src/node.ts
7
+ import {
8
+ requireLayoutSystem
9
+ } from "@illusions-lab/mdi-export-profile";
10
+ function preparePdfExport(source, profile) {
11
+ if (typeof source !== "string") throw new TypeError("source must be a string");
12
+ if (profile !== void 0) requireLayoutSystem(profile);
13
+ const writingMode = parse(source).document.frontmatter?.entries.find(
14
+ (entry) => entry.key === "writing-mode" || entry.key === "writingMode"
15
+ )?.value;
16
+ return {
17
+ html: renderHtml(source),
18
+ profile,
19
+ sourceWritingMode: writingMode === "vertical" || writingMode === "horizontal" ? writingMode : void 0
20
+ };
21
+ }
22
+ function preparePdfExportWithDiagnostics(source, profile) {
23
+ if (typeof source !== "string") throw new TypeError("source must be a string");
24
+ if (profile !== void 0) requireLayoutSystem(profile);
25
+ const parsed = parse(source);
26
+ const writingMode = parsed.document.frontmatter?.entries.find(
27
+ (entry) => entry.key === "writing-mode" || entry.key === "writingMode"
28
+ )?.value;
29
+ return {
30
+ output: {
31
+ html: renderHtml(source),
32
+ profile,
33
+ sourceWritingMode: writingMode === "vertical" || writingMode === "horizontal" ? writingMode : void 0
34
+ },
35
+ document: parsed.document,
36
+ diagnostics: parsed.diagnostics,
37
+ headings: headingsFromDocument(parsed.document)
38
+ };
39
+ }
40
+ async function renderPdfWithChromium(source, profile, adapter) {
41
+ const request = preparePdfExport(source, profile);
42
+ const chromiumAdapter = adapter ?? await loadPlaywrightPdfAdapter();
43
+ return chromiumAdapter.renderHtmlToPdf(
44
+ request.html,
45
+ request.profile,
46
+ request.sourceWritingMode
47
+ );
48
+ }
49
+ async function renderPdfWithChromiumWithDiagnostics(source, profile, adapter) {
50
+ const prepared = preparePdfExportWithDiagnostics(source, profile);
51
+ const chromiumAdapter = adapter ?? await loadPlaywrightPdfAdapter();
52
+ return {
53
+ ...prepared,
54
+ output: await chromiumAdapter.renderHtmlToPdf(
55
+ prepared.output.html,
56
+ prepared.output.profile,
57
+ prepared.output.sourceWritingMode
58
+ )
59
+ };
60
+ }
61
+ function headingsFromDocument(document) {
62
+ const headings = [];
63
+ const visit = (nodes) => {
64
+ for (const node of nodes) {
65
+ if (node.type === "heading" && isHeadingDepth(node.depth)) {
66
+ headings.push({ depth: node.depth, text: plainText(node), span: node.span, node });
67
+ }
68
+ if (node.children) visit(node.children);
69
+ }
70
+ };
71
+ visit(document.children);
72
+ return headings;
73
+ }
74
+ function isHeadingDepth(value) {
75
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 6;
76
+ }
77
+ function plainText(node) {
78
+ if (node.type === "ruby" && typeof node.base === "string") return node.base;
79
+ return (typeof node.value === "string" ? node.value : "") + (node.children?.map(plainText).join("") ?? "");
80
+ }
81
+ async function loadPlaywrightPdfAdapter() {
82
+ const packageName = "@illusions-lab/mdi-to-pdf";
83
+ try {
84
+ return await import(packageName);
85
+ } catch (error) {
86
+ throw new Error(
87
+ "PDF export needs @illusions-lab/mdi-to-pdf, or pass an Electron-compatible adapter to renderPdfWithChromium().",
88
+ { cause: error }
89
+ );
90
+ }
91
+ }
92
+ export {
93
+ preparePdfExport,
94
+ preparePdfExportWithDiagnostics,
95
+ renderPdfWithChromium,
96
+ renderPdfWithChromiumWithDiagnostics
97
+ };