@illusions-lab/mdi 2.0.18 → 2.0.19
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/README.md +23 -0
- package/dist/{chunk-WEJ62HS6.js → chunk-JTDOLXFG.js} +94 -0
- package/dist/index.cjs +100 -0
- package/dist/index.d.cts +70 -1
- package/dist/index.d.ts +70 -1
- package/dist/index.js +13 -1
- package/dist/node.cjs +1 -0
- package/dist/node.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -79,6 +79,29 @@ Applications should treat the IR version as a wire-protocol version. They
|
|
|
79
79
|
must not infer grammar rules from object shapes or silently accept an
|
|
80
80
|
unsupported version.
|
|
81
81
|
|
|
82
|
+
## Searchable text blocks
|
|
83
|
+
|
|
84
|
+
`getMdiTextBlocks(source)` returns Rust-projected heading, paragraph, list,
|
|
85
|
+
blockquote, code, table, footnote, and HTML text in source order. Positions
|
|
86
|
+
such as `3:18` count one-based Unicode grapheme clusters; ruby readings are a
|
|
87
|
+
separate annotation channel anchored to the base-text range.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { getMdiTextBlocks, sourceSpansForTextRange } from "@illusions-lab/mdi";
|
|
91
|
+
|
|
92
|
+
const result = getMdiTextBlocks("{東京|とうきょう}");
|
|
93
|
+
const block = result.blocks[0];
|
|
94
|
+
console.log(block.text); // 東京
|
|
95
|
+
console.log(block.annotations[0].anchor); // { start: "1:1", end: "1:3" }
|
|
96
|
+
console.log(sourceSpansForTextRange(block, { start: "1:1", end: "1:3" }));
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Each source-derived grapheme is represented by a `sourceMap.runs` boundary;
|
|
100
|
+
table tabs/newlines and multi-paragraph joiners appear in `synthetic` and do
|
|
101
|
+
not receive invented source spans. `parseMdiTextPosition`,
|
|
102
|
+
`formatMdiTextPosition`, and `formatMdiTextRange` provide stateless coordinate
|
|
103
|
+
helpers.
|
|
104
|
+
|
|
82
105
|
## Rendering
|
|
83
106
|
|
|
84
107
|
Rendering starts from the same Rust IR. Canonical MDI, plain text, HTML, EPUB,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as mdiCore from "@illusions-lab/mdi-core";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
var {
|
|
5
|
+
getMdiTextBlocksJson,
|
|
5
6
|
parseMdiSyntaxJson,
|
|
6
7
|
renderHtml: renderHtmlFromRust,
|
|
7
8
|
renderEpub: renderEpubFromRust,
|
|
@@ -24,6 +25,7 @@ function requireLayoutSystem(profile) {
|
|
|
24
25
|
}
|
|
25
26
|
var MDI_SPEC_VERSION = "2.0";
|
|
26
27
|
var MDI_IR_VERSION = "1.0";
|
|
28
|
+
var MDI_TEXT_PROJECTION_VERSION = "1.0";
|
|
27
29
|
function parse(source) {
|
|
28
30
|
if (typeof source !== "string") throw new TypeError("source must be a string");
|
|
29
31
|
const result = JSON.parse(parseMdiSyntaxJson(source));
|
|
@@ -32,6 +34,92 @@ function parse(source) {
|
|
|
32
34
|
}
|
|
33
35
|
return result;
|
|
34
36
|
}
|
|
37
|
+
function getMdiTextBlocks(source) {
|
|
38
|
+
if (typeof source !== "string") throw new TypeError("source must be a string");
|
|
39
|
+
const result = JSON.parse(getMdiTextBlocksJson(source));
|
|
40
|
+
if (result.projectionVersion !== MDI_TEXT_PROJECTION_VERSION) {
|
|
41
|
+
throw new Error(`Unsupported MDI text projection version: ${String(result.projectionVersion)}`);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function parseMdiTextPosition(position) {
|
|
46
|
+
if (typeof position !== "string") throw new TypeError("position must be a string");
|
|
47
|
+
const match = /^([1-9]\d*):([1-9]\d*)$/.exec(position);
|
|
48
|
+
if (!match) throw new RangeError(`Invalid MDI text position: ${position}`);
|
|
49
|
+
const block = Number(match[1]);
|
|
50
|
+
const character = Number(match[2]);
|
|
51
|
+
if (!Number.isSafeInteger(block) || !Number.isSafeInteger(character)) {
|
|
52
|
+
throw new RangeError(`Invalid MDI text position: ${position}`);
|
|
53
|
+
}
|
|
54
|
+
return { block, character };
|
|
55
|
+
}
|
|
56
|
+
function formatMdiTextPosition(position) {
|
|
57
|
+
if (!position || typeof position !== "object") {
|
|
58
|
+
throw new TypeError("position must be an object");
|
|
59
|
+
}
|
|
60
|
+
if (!isPositiveSafeInteger(position.block) || !isPositiveSafeInteger(position.character)) {
|
|
61
|
+
throw new RangeError("position.block and position.character must be positive safe integers");
|
|
62
|
+
}
|
|
63
|
+
return `${position.block}:${position.character}`;
|
|
64
|
+
}
|
|
65
|
+
function formatMdiTextRange(range) {
|
|
66
|
+
if (!range || typeof range !== "object") throw new TypeError("range must be an object");
|
|
67
|
+
const start = parseMdiTextPosition(range.start);
|
|
68
|
+
const end = parseMdiTextPosition(range.end);
|
|
69
|
+
if (start.block !== end.block || end.character < start.character) {
|
|
70
|
+
throw new RangeError("range must be ordered within one text block");
|
|
71
|
+
}
|
|
72
|
+
return `${range.start}-${range.end}`;
|
|
73
|
+
}
|
|
74
|
+
function sourceSpansForTextRange(block, range) {
|
|
75
|
+
if (!block || typeof block !== "object") throw new TypeError("block must be an object");
|
|
76
|
+
const start = parseMdiTextPosition(range.start);
|
|
77
|
+
const end = parseMdiTextPosition(range.end);
|
|
78
|
+
if (start.block !== block.index || end.block !== block.index) {
|
|
79
|
+
throw new RangeError("range must belong to the supplied text block");
|
|
80
|
+
}
|
|
81
|
+
const blockEnd = parseMdiTextPosition(block.range.end).character;
|
|
82
|
+
if (end.character < start.character || start.character < 1 || end.character > blockEnd) {
|
|
83
|
+
throw new RangeError("range falls outside the supplied text block");
|
|
84
|
+
}
|
|
85
|
+
const spans = [];
|
|
86
|
+
let previousRunEnd = 1;
|
|
87
|
+
for (const run of block.sourceMap.runs) {
|
|
88
|
+
const runStartPosition = parseMdiTextPosition(run.range.start);
|
|
89
|
+
const runEndPosition = parseMdiTextPosition(run.range.end);
|
|
90
|
+
const runStart = runStartPosition.character;
|
|
91
|
+
const runEnd = runEndPosition.character;
|
|
92
|
+
const invalidRange = runStartPosition.block !== block.index || runEndPosition.block !== block.index || runEnd < runStart || runStart < previousRunEnd || runEnd > blockEnd;
|
|
93
|
+
const invalidBoundaries = run.sourceBoundaries.length !== runEnd - runStart + 1 || run.sourceBoundaries.some((boundary) => !Number.isSafeInteger(boundary) || boundary < 0) || run.sourceBoundaries.some((boundary, index) => index > 0 && boundary < run.sourceBoundaries[index - 1]) || block.span !== void 0 && run.sourceBoundaries.some(
|
|
94
|
+
(boundary) => boundary < block.span.startByte || boundary > block.span.endByte
|
|
95
|
+
);
|
|
96
|
+
if (invalidRange || invalidBoundaries) {
|
|
97
|
+
throw new Error("Invalid MDI text source run");
|
|
98
|
+
}
|
|
99
|
+
previousRunEnd = runEnd;
|
|
100
|
+
const overlapStart = Math.max(start.character, runStart);
|
|
101
|
+
const overlapEnd = Math.min(end.character, runEnd);
|
|
102
|
+
for (let character = overlapStart; character < overlapEnd; character += 1) {
|
|
103
|
+
const offset = character - runStart;
|
|
104
|
+
appendMergedSourceSpan(spans, {
|
|
105
|
+
startByte: run.sourceBoundaries[offset],
|
|
106
|
+
endByte: run.sourceBoundaries[offset + 1]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return spans;
|
|
111
|
+
}
|
|
112
|
+
function appendMergedSourceSpan(spans, next) {
|
|
113
|
+
const previous = spans.at(-1);
|
|
114
|
+
if (previous?.endByte === next.startByte) {
|
|
115
|
+
previous.endByte = next.endByte;
|
|
116
|
+
} else {
|
|
117
|
+
spans.push(next);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function isPositiveSafeInteger(value) {
|
|
121
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
122
|
+
}
|
|
35
123
|
function renderHtml(source, options) {
|
|
36
124
|
assertSource(source);
|
|
37
125
|
assertHtmlOptions(options);
|
|
@@ -366,7 +454,13 @@ export {
|
|
|
366
454
|
initializeMdi,
|
|
367
455
|
MDI_SPEC_VERSION,
|
|
368
456
|
MDI_IR_VERSION,
|
|
457
|
+
MDI_TEXT_PROJECTION_VERSION,
|
|
369
458
|
parse,
|
|
459
|
+
getMdiTextBlocks,
|
|
460
|
+
parseMdiTextPosition,
|
|
461
|
+
formatMdiTextPosition,
|
|
462
|
+
formatMdiTextRange,
|
|
463
|
+
sourceSpansForTextRange,
|
|
370
464
|
renderHtml,
|
|
371
465
|
renderHtmlWithDiagnostics,
|
|
372
466
|
prepareRender,
|
package/dist/index.cjs
CHANGED
|
@@ -32,9 +32,14 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
MDI_IR_VERSION: () => MDI_IR_VERSION,
|
|
34
34
|
MDI_SPEC_VERSION: () => MDI_SPEC_VERSION,
|
|
35
|
+
MDI_TEXT_PROJECTION_VERSION: () => MDI_TEXT_PROJECTION_VERSION,
|
|
36
|
+
formatMdiTextPosition: () => formatMdiTextPosition,
|
|
37
|
+
formatMdiTextRange: () => formatMdiTextRange,
|
|
38
|
+
getMdiTextBlocks: () => getMdiTextBlocks,
|
|
35
39
|
initializeMdi: () => initializeMdi,
|
|
36
40
|
parse: () => parse,
|
|
37
41
|
parseMdiSyntax: () => parseMdiSyntax,
|
|
42
|
+
parseMdiTextPosition: () => parseMdiTextPosition,
|
|
38
43
|
prepareRender: () => prepareRender,
|
|
39
44
|
renderDocx: () => renderDocx,
|
|
40
45
|
renderDocxWithDiagnostics: () => renderDocxWithDiagnostics,
|
|
@@ -49,12 +54,14 @@ __export(index_exports, {
|
|
|
49
54
|
renderTextFormatWithDiagnostics: () => renderTextFormatWithDiagnostics,
|
|
50
55
|
renderTextWithDiagnostics: () => renderTextWithDiagnostics,
|
|
51
56
|
serializeMdi: () => serializeMdi,
|
|
57
|
+
sourceSpansForTextRange: () => sourceSpansForTextRange,
|
|
52
58
|
toPublicationMdast: () => toPublicationMdast
|
|
53
59
|
});
|
|
54
60
|
module.exports = __toCommonJS(index_exports);
|
|
55
61
|
var mdiCore = __toESM(require("@illusions-lab/mdi-core"), 1);
|
|
56
62
|
var import_yaml = require("yaml");
|
|
57
63
|
var {
|
|
64
|
+
getMdiTextBlocksJson,
|
|
58
65
|
parseMdiSyntaxJson,
|
|
59
66
|
renderHtml: renderHtmlFromRust,
|
|
60
67
|
renderEpub: renderEpubFromRust,
|
|
@@ -77,6 +84,7 @@ function requireLayoutSystem(profile) {
|
|
|
77
84
|
}
|
|
78
85
|
var MDI_SPEC_VERSION = "2.0";
|
|
79
86
|
var MDI_IR_VERSION = "1.0";
|
|
87
|
+
var MDI_TEXT_PROJECTION_VERSION = "1.0";
|
|
80
88
|
function parse(source) {
|
|
81
89
|
if (typeof source !== "string") throw new TypeError("source must be a string");
|
|
82
90
|
const result = JSON.parse(parseMdiSyntaxJson(source));
|
|
@@ -85,6 +93,92 @@ function parse(source) {
|
|
|
85
93
|
}
|
|
86
94
|
return result;
|
|
87
95
|
}
|
|
96
|
+
function getMdiTextBlocks(source) {
|
|
97
|
+
if (typeof source !== "string") throw new TypeError("source must be a string");
|
|
98
|
+
const result = JSON.parse(getMdiTextBlocksJson(source));
|
|
99
|
+
if (result.projectionVersion !== MDI_TEXT_PROJECTION_VERSION) {
|
|
100
|
+
throw new Error(`Unsupported MDI text projection version: ${String(result.projectionVersion)}`);
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function parseMdiTextPosition(position) {
|
|
105
|
+
if (typeof position !== "string") throw new TypeError("position must be a string");
|
|
106
|
+
const match = /^([1-9]\d*):([1-9]\d*)$/.exec(position);
|
|
107
|
+
if (!match) throw new RangeError(`Invalid MDI text position: ${position}`);
|
|
108
|
+
const block = Number(match[1]);
|
|
109
|
+
const character = Number(match[2]);
|
|
110
|
+
if (!Number.isSafeInteger(block) || !Number.isSafeInteger(character)) {
|
|
111
|
+
throw new RangeError(`Invalid MDI text position: ${position}`);
|
|
112
|
+
}
|
|
113
|
+
return { block, character };
|
|
114
|
+
}
|
|
115
|
+
function formatMdiTextPosition(position) {
|
|
116
|
+
if (!position || typeof position !== "object") {
|
|
117
|
+
throw new TypeError("position must be an object");
|
|
118
|
+
}
|
|
119
|
+
if (!isPositiveSafeInteger(position.block) || !isPositiveSafeInteger(position.character)) {
|
|
120
|
+
throw new RangeError("position.block and position.character must be positive safe integers");
|
|
121
|
+
}
|
|
122
|
+
return `${position.block}:${position.character}`;
|
|
123
|
+
}
|
|
124
|
+
function formatMdiTextRange(range) {
|
|
125
|
+
if (!range || typeof range !== "object") throw new TypeError("range must be an object");
|
|
126
|
+
const start = parseMdiTextPosition(range.start);
|
|
127
|
+
const end = parseMdiTextPosition(range.end);
|
|
128
|
+
if (start.block !== end.block || end.character < start.character) {
|
|
129
|
+
throw new RangeError("range must be ordered within one text block");
|
|
130
|
+
}
|
|
131
|
+
return `${range.start}-${range.end}`;
|
|
132
|
+
}
|
|
133
|
+
function sourceSpansForTextRange(block, range) {
|
|
134
|
+
if (!block || typeof block !== "object") throw new TypeError("block must be an object");
|
|
135
|
+
const start = parseMdiTextPosition(range.start);
|
|
136
|
+
const end = parseMdiTextPosition(range.end);
|
|
137
|
+
if (start.block !== block.index || end.block !== block.index) {
|
|
138
|
+
throw new RangeError("range must belong to the supplied text block");
|
|
139
|
+
}
|
|
140
|
+
const blockEnd = parseMdiTextPosition(block.range.end).character;
|
|
141
|
+
if (end.character < start.character || start.character < 1 || end.character > blockEnd) {
|
|
142
|
+
throw new RangeError("range falls outside the supplied text block");
|
|
143
|
+
}
|
|
144
|
+
const spans = [];
|
|
145
|
+
let previousRunEnd = 1;
|
|
146
|
+
for (const run of block.sourceMap.runs) {
|
|
147
|
+
const runStartPosition = parseMdiTextPosition(run.range.start);
|
|
148
|
+
const runEndPosition = parseMdiTextPosition(run.range.end);
|
|
149
|
+
const runStart = runStartPosition.character;
|
|
150
|
+
const runEnd = runEndPosition.character;
|
|
151
|
+
const invalidRange = runStartPosition.block !== block.index || runEndPosition.block !== block.index || runEnd < runStart || runStart < previousRunEnd || runEnd > blockEnd;
|
|
152
|
+
const invalidBoundaries = run.sourceBoundaries.length !== runEnd - runStart + 1 || run.sourceBoundaries.some((boundary) => !Number.isSafeInteger(boundary) || boundary < 0) || run.sourceBoundaries.some((boundary, index) => index > 0 && boundary < run.sourceBoundaries[index - 1]) || block.span !== void 0 && run.sourceBoundaries.some(
|
|
153
|
+
(boundary) => boundary < block.span.startByte || boundary > block.span.endByte
|
|
154
|
+
);
|
|
155
|
+
if (invalidRange || invalidBoundaries) {
|
|
156
|
+
throw new Error("Invalid MDI text source run");
|
|
157
|
+
}
|
|
158
|
+
previousRunEnd = runEnd;
|
|
159
|
+
const overlapStart = Math.max(start.character, runStart);
|
|
160
|
+
const overlapEnd = Math.min(end.character, runEnd);
|
|
161
|
+
for (let character = overlapStart; character < overlapEnd; character += 1) {
|
|
162
|
+
const offset = character - runStart;
|
|
163
|
+
appendMergedSourceSpan(spans, {
|
|
164
|
+
startByte: run.sourceBoundaries[offset],
|
|
165
|
+
endByte: run.sourceBoundaries[offset + 1]
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return spans;
|
|
170
|
+
}
|
|
171
|
+
function appendMergedSourceSpan(spans, next) {
|
|
172
|
+
const previous = spans.at(-1);
|
|
173
|
+
if (previous?.endByte === next.startByte) {
|
|
174
|
+
previous.endByte = next.endByte;
|
|
175
|
+
} else {
|
|
176
|
+
spans.push(next);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function isPositiveSafeInteger(value) {
|
|
180
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
181
|
+
}
|
|
88
182
|
function renderHtml(source, options) {
|
|
89
183
|
assertSource(source);
|
|
90
184
|
assertHtmlOptions(options);
|
|
@@ -418,9 +512,14 @@ var parseMdiSyntax = parse;
|
|
|
418
512
|
0 && (module.exports = {
|
|
419
513
|
MDI_IR_VERSION,
|
|
420
514
|
MDI_SPEC_VERSION,
|
|
515
|
+
MDI_TEXT_PROJECTION_VERSION,
|
|
516
|
+
formatMdiTextPosition,
|
|
517
|
+
formatMdiTextRange,
|
|
518
|
+
getMdiTextBlocks,
|
|
421
519
|
initializeMdi,
|
|
422
520
|
parse,
|
|
423
521
|
parseMdiSyntax,
|
|
522
|
+
parseMdiTextPosition,
|
|
424
523
|
prepareRender,
|
|
425
524
|
renderDocx,
|
|
426
525
|
renderDocxWithDiagnostics,
|
|
@@ -435,5 +534,6 @@ var parseMdiSyntax = parse;
|
|
|
435
534
|
renderTextFormatWithDiagnostics,
|
|
436
535
|
renderTextWithDiagnostics,
|
|
437
536
|
serializeMdi,
|
|
537
|
+
sourceSpansForTextRange,
|
|
438
538
|
toPublicationMdast
|
|
439
539
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -76,6 +76,8 @@ type MdiDocxExportProfile = ExportProfile & {
|
|
|
76
76
|
declare const MDI_SPEC_VERSION: "2.0";
|
|
77
77
|
/** Version of the complete Rust-owned document IR. */
|
|
78
78
|
declare const MDI_IR_VERSION: "1.0";
|
|
79
|
+
/** Version of the Rust-owned searchable text projection. */
|
|
80
|
+
declare const MDI_TEXT_PROJECTION_VERSION: "1.0";
|
|
79
81
|
interface MdiParserCapabilities {
|
|
80
82
|
mdi: boolean;
|
|
81
83
|
commonMark: boolean;
|
|
@@ -89,6 +91,57 @@ interface MdiSourceSpan {
|
|
|
89
91
|
/** Exclusive UTF-8 byte offset. */
|
|
90
92
|
endByte: number;
|
|
91
93
|
}
|
|
94
|
+
/** A canonical one-based `block:grapheme` text position. */
|
|
95
|
+
type MdiTextPosition = `${number}:${number}`;
|
|
96
|
+
interface MdiTextPositionValue {
|
|
97
|
+
block: number;
|
|
98
|
+
character: number;
|
|
99
|
+
}
|
|
100
|
+
interface MdiTextRange {
|
|
101
|
+
/** Inclusive. */
|
|
102
|
+
start: MdiTextPosition;
|
|
103
|
+
/** Exclusive. */
|
|
104
|
+
end: MdiTextPosition;
|
|
105
|
+
}
|
|
106
|
+
interface MdiTextSourceRun {
|
|
107
|
+
range: MdiTextRange;
|
|
108
|
+
/** One UTF-8 source boundary per grapheme, plus the final boundary. */
|
|
109
|
+
sourceBoundaries: number[];
|
|
110
|
+
}
|
|
111
|
+
interface MdiTextSourceMap {
|
|
112
|
+
runs: MdiTextSourceRun[];
|
|
113
|
+
synthetic: MdiTextRange[];
|
|
114
|
+
unmapped: MdiTextRange[];
|
|
115
|
+
}
|
|
116
|
+
type MdiAnnotationSourceMap = MdiTextSourceMap;
|
|
117
|
+
interface MdiTextAnnotation {
|
|
118
|
+
kind: "rubyReading";
|
|
119
|
+
text: string;
|
|
120
|
+
anchor: MdiTextRange;
|
|
121
|
+
span?: MdiSourceSpan;
|
|
122
|
+
sourceMap: MdiAnnotationSourceMap;
|
|
123
|
+
}
|
|
124
|
+
interface MdiTextBlock {
|
|
125
|
+
/** One-based source-order block number. */
|
|
126
|
+
index: number;
|
|
127
|
+
kind: "heading" | "paragraph" | "listItem" | "blockquote" | "code" | "table" | "footnote" | "html" | "other";
|
|
128
|
+
text: string;
|
|
129
|
+
range: MdiTextRange;
|
|
130
|
+
span?: MdiSourceSpan;
|
|
131
|
+
sourceMap: MdiTextSourceMap;
|
|
132
|
+
annotations: MdiTextAnnotation[];
|
|
133
|
+
node: MdiNode;
|
|
134
|
+
}
|
|
135
|
+
interface MdiTextBlocksResult {
|
|
136
|
+
projectionVersion: "1.0";
|
|
137
|
+
positionEncoding: "unicode-grapheme-cluster-1-based";
|
|
138
|
+
irVersion: typeof MDI_IR_VERSION;
|
|
139
|
+
syntaxVersion: typeof MDI_SPEC_VERSION;
|
|
140
|
+
capabilities: MdiParserCapabilities;
|
|
141
|
+
blocks: MdiTextBlock[];
|
|
142
|
+
document: MdiDocument;
|
|
143
|
+
diagnostics: MdiDiagnostic[];
|
|
144
|
+
}
|
|
92
145
|
interface MdiDiagnostic {
|
|
93
146
|
severity: "warning" | "error";
|
|
94
147
|
code: string;
|
|
@@ -178,6 +231,22 @@ type MdiSyntaxDocument = MdiDocument;
|
|
|
178
231
|
* language-neutral document IR. JavaScript performs no grammar work.
|
|
179
232
|
*/
|
|
180
233
|
declare function parse(source: string): MdiSyntaxParseResult;
|
|
234
|
+
/**
|
|
235
|
+
* Parse once in Rust and return source-order plaintext blocks, annotations,
|
|
236
|
+
* and grapheme-precise UTF-8 source maps alongside the complete document IR.
|
|
237
|
+
*/
|
|
238
|
+
declare function getMdiTextBlocks(source: string): MdiTextBlocksResult;
|
|
239
|
+
/** Parse and validate a canonical one-based `block:character` position. */
|
|
240
|
+
declare function parseMdiTextPosition(position: string): MdiTextPositionValue;
|
|
241
|
+
/** Format a validated one-based text position. */
|
|
242
|
+
declare function formatMdiTextPosition(position: MdiTextPositionValue): MdiTextPosition;
|
|
243
|
+
/** Format a canonical full `start-end` range such as `3:18-3:24`. */
|
|
244
|
+
declare function formatMdiTextRange(range: MdiTextRange): string;
|
|
245
|
+
/**
|
|
246
|
+
* Resolve a text range to its source-derived UTF-8 spans. Synthetic table or
|
|
247
|
+
* paragraph separators are deliberately omitted.
|
|
248
|
+
*/
|
|
249
|
+
declare function sourceSpansForTextRange(block: MdiTextBlock, range: MdiTextRange): MdiSourceSpan[];
|
|
181
250
|
/** Render complete `.mdi` source to standalone semantic HTML in Rust. */
|
|
182
251
|
declare function renderHtml(source: string, options?: MdiHtmlRenderOptions): string;
|
|
183
252
|
/**
|
|
@@ -248,4 +317,4 @@ declare function renderTextFormatWithDiagnostics(source: string, format: MdiText
|
|
|
248
317
|
/** @deprecated Use {@link parse}; it now parses the complete document. */
|
|
249
318
|
declare const parseMdiSyntax: typeof parse;
|
|
250
319
|
|
|
251
|
-
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 MdiPublicationFrontmatter, type MdiPublicationRoot, type MdiRenderResult, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextFormat, initializeMdi, parse, parseMdiSyntax, prepareRender, renderDocx, renderDocxWithDiagnostics, renderDocxWithProfile, renderEpub, renderEpubWithDiagnostics, renderEpubWithProfile, renderHtml, renderHtmlWithDiagnostics, renderText, renderTextFormat, renderTextFormatWithDiagnostics, renderTextWithDiagnostics, serializeMdi, toPublicationMdast };
|
|
320
|
+
export { MDI_IR_VERSION, MDI_SPEC_VERSION, MDI_TEXT_PROJECTION_VERSION, type MdiAnnotationSourceMap, type MdiDiagnostic, type MdiDocument, type MdiDocxExportProfile, type MdiEpubExportOptions, type MdiFrontmatter, type MdiHeading, type MdiHtmlRenderOptions, type MdiNode, type MdiParserCapabilities, type MdiPublicationFrontmatter, type MdiPublicationRoot, type MdiRenderResult, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextAnnotation, type MdiTextBlock, type MdiTextBlocksResult, type MdiTextFormat, type MdiTextPosition, type MdiTextPositionValue, type MdiTextRange, type MdiTextSourceMap, type MdiTextSourceRun, formatMdiTextPosition, formatMdiTextRange, getMdiTextBlocks, initializeMdi, parse, parseMdiSyntax, parseMdiTextPosition, prepareRender, renderDocx, renderDocxWithDiagnostics, renderDocxWithProfile, renderEpub, renderEpubWithDiagnostics, renderEpubWithProfile, renderHtml, renderHtmlWithDiagnostics, renderText, renderTextFormat, renderTextFormatWithDiagnostics, renderTextWithDiagnostics, serializeMdi, sourceSpansForTextRange, toPublicationMdast };
|
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,8 @@ type MdiDocxExportProfile = ExportProfile & {
|
|
|
76
76
|
declare const MDI_SPEC_VERSION: "2.0";
|
|
77
77
|
/** Version of the complete Rust-owned document IR. */
|
|
78
78
|
declare const MDI_IR_VERSION: "1.0";
|
|
79
|
+
/** Version of the Rust-owned searchable text projection. */
|
|
80
|
+
declare const MDI_TEXT_PROJECTION_VERSION: "1.0";
|
|
79
81
|
interface MdiParserCapabilities {
|
|
80
82
|
mdi: boolean;
|
|
81
83
|
commonMark: boolean;
|
|
@@ -89,6 +91,57 @@ interface MdiSourceSpan {
|
|
|
89
91
|
/** Exclusive UTF-8 byte offset. */
|
|
90
92
|
endByte: number;
|
|
91
93
|
}
|
|
94
|
+
/** A canonical one-based `block:grapheme` text position. */
|
|
95
|
+
type MdiTextPosition = `${number}:${number}`;
|
|
96
|
+
interface MdiTextPositionValue {
|
|
97
|
+
block: number;
|
|
98
|
+
character: number;
|
|
99
|
+
}
|
|
100
|
+
interface MdiTextRange {
|
|
101
|
+
/** Inclusive. */
|
|
102
|
+
start: MdiTextPosition;
|
|
103
|
+
/** Exclusive. */
|
|
104
|
+
end: MdiTextPosition;
|
|
105
|
+
}
|
|
106
|
+
interface MdiTextSourceRun {
|
|
107
|
+
range: MdiTextRange;
|
|
108
|
+
/** One UTF-8 source boundary per grapheme, plus the final boundary. */
|
|
109
|
+
sourceBoundaries: number[];
|
|
110
|
+
}
|
|
111
|
+
interface MdiTextSourceMap {
|
|
112
|
+
runs: MdiTextSourceRun[];
|
|
113
|
+
synthetic: MdiTextRange[];
|
|
114
|
+
unmapped: MdiTextRange[];
|
|
115
|
+
}
|
|
116
|
+
type MdiAnnotationSourceMap = MdiTextSourceMap;
|
|
117
|
+
interface MdiTextAnnotation {
|
|
118
|
+
kind: "rubyReading";
|
|
119
|
+
text: string;
|
|
120
|
+
anchor: MdiTextRange;
|
|
121
|
+
span?: MdiSourceSpan;
|
|
122
|
+
sourceMap: MdiAnnotationSourceMap;
|
|
123
|
+
}
|
|
124
|
+
interface MdiTextBlock {
|
|
125
|
+
/** One-based source-order block number. */
|
|
126
|
+
index: number;
|
|
127
|
+
kind: "heading" | "paragraph" | "listItem" | "blockquote" | "code" | "table" | "footnote" | "html" | "other";
|
|
128
|
+
text: string;
|
|
129
|
+
range: MdiTextRange;
|
|
130
|
+
span?: MdiSourceSpan;
|
|
131
|
+
sourceMap: MdiTextSourceMap;
|
|
132
|
+
annotations: MdiTextAnnotation[];
|
|
133
|
+
node: MdiNode;
|
|
134
|
+
}
|
|
135
|
+
interface MdiTextBlocksResult {
|
|
136
|
+
projectionVersion: "1.0";
|
|
137
|
+
positionEncoding: "unicode-grapheme-cluster-1-based";
|
|
138
|
+
irVersion: typeof MDI_IR_VERSION;
|
|
139
|
+
syntaxVersion: typeof MDI_SPEC_VERSION;
|
|
140
|
+
capabilities: MdiParserCapabilities;
|
|
141
|
+
blocks: MdiTextBlock[];
|
|
142
|
+
document: MdiDocument;
|
|
143
|
+
diagnostics: MdiDiagnostic[];
|
|
144
|
+
}
|
|
92
145
|
interface MdiDiagnostic {
|
|
93
146
|
severity: "warning" | "error";
|
|
94
147
|
code: string;
|
|
@@ -178,6 +231,22 @@ type MdiSyntaxDocument = MdiDocument;
|
|
|
178
231
|
* language-neutral document IR. JavaScript performs no grammar work.
|
|
179
232
|
*/
|
|
180
233
|
declare function parse(source: string): MdiSyntaxParseResult;
|
|
234
|
+
/**
|
|
235
|
+
* Parse once in Rust and return source-order plaintext blocks, annotations,
|
|
236
|
+
* and grapheme-precise UTF-8 source maps alongside the complete document IR.
|
|
237
|
+
*/
|
|
238
|
+
declare function getMdiTextBlocks(source: string): MdiTextBlocksResult;
|
|
239
|
+
/** Parse and validate a canonical one-based `block:character` position. */
|
|
240
|
+
declare function parseMdiTextPosition(position: string): MdiTextPositionValue;
|
|
241
|
+
/** Format a validated one-based text position. */
|
|
242
|
+
declare function formatMdiTextPosition(position: MdiTextPositionValue): MdiTextPosition;
|
|
243
|
+
/** Format a canonical full `start-end` range such as `3:18-3:24`. */
|
|
244
|
+
declare function formatMdiTextRange(range: MdiTextRange): string;
|
|
245
|
+
/**
|
|
246
|
+
* Resolve a text range to its source-derived UTF-8 spans. Synthetic table or
|
|
247
|
+
* paragraph separators are deliberately omitted.
|
|
248
|
+
*/
|
|
249
|
+
declare function sourceSpansForTextRange(block: MdiTextBlock, range: MdiTextRange): MdiSourceSpan[];
|
|
181
250
|
/** Render complete `.mdi` source to standalone semantic HTML in Rust. */
|
|
182
251
|
declare function renderHtml(source: string, options?: MdiHtmlRenderOptions): string;
|
|
183
252
|
/**
|
|
@@ -248,4 +317,4 @@ declare function renderTextFormatWithDiagnostics(source: string, format: MdiText
|
|
|
248
317
|
/** @deprecated Use {@link parse}; it now parses the complete document. */
|
|
249
318
|
declare const parseMdiSyntax: typeof parse;
|
|
250
319
|
|
|
251
|
-
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 MdiPublicationFrontmatter, type MdiPublicationRoot, type MdiRenderResult, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextFormat, initializeMdi, parse, parseMdiSyntax, prepareRender, renderDocx, renderDocxWithDiagnostics, renderDocxWithProfile, renderEpub, renderEpubWithDiagnostics, renderEpubWithProfile, renderHtml, renderHtmlWithDiagnostics, renderText, renderTextFormat, renderTextFormatWithDiagnostics, renderTextWithDiagnostics, serializeMdi, toPublicationMdast };
|
|
320
|
+
export { MDI_IR_VERSION, MDI_SPEC_VERSION, MDI_TEXT_PROJECTION_VERSION, type MdiAnnotationSourceMap, type MdiDiagnostic, type MdiDocument, type MdiDocxExportProfile, type MdiEpubExportOptions, type MdiFrontmatter, type MdiHeading, type MdiHtmlRenderOptions, type MdiNode, type MdiParserCapabilities, type MdiPublicationFrontmatter, type MdiPublicationRoot, type MdiRenderResult, type MdiRubyReading, type MdiSourceSpan, type MdiSyntaxDocument, type MdiSyntaxParseResult, type MdiTextAnnotation, type MdiTextBlock, type MdiTextBlocksResult, type MdiTextFormat, type MdiTextPosition, type MdiTextPositionValue, type MdiTextRange, type MdiTextSourceMap, type MdiTextSourceRun, formatMdiTextPosition, formatMdiTextRange, getMdiTextBlocks, initializeMdi, parse, parseMdiSyntax, parseMdiTextPosition, prepareRender, renderDocx, renderDocxWithDiagnostics, renderDocxWithProfile, renderEpub, renderEpubWithDiagnostics, renderEpubWithProfile, renderHtml, renderHtmlWithDiagnostics, renderText, renderTextFormat, renderTextFormatWithDiagnostics, renderTextWithDiagnostics, serializeMdi, sourceSpansForTextRange, toPublicationMdast };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MDI_IR_VERSION,
|
|
3
3
|
MDI_SPEC_VERSION,
|
|
4
|
+
MDI_TEXT_PROJECTION_VERSION,
|
|
5
|
+
formatMdiTextPosition,
|
|
6
|
+
formatMdiTextRange,
|
|
7
|
+
getMdiTextBlocks,
|
|
4
8
|
initializeMdi,
|
|
5
9
|
parse,
|
|
6
10
|
parseMdiSyntax,
|
|
11
|
+
parseMdiTextPosition,
|
|
7
12
|
prepareRender,
|
|
8
13
|
renderDocx,
|
|
9
14
|
renderDocxWithDiagnostics,
|
|
@@ -18,14 +23,20 @@ import {
|
|
|
18
23
|
renderTextFormatWithDiagnostics,
|
|
19
24
|
renderTextWithDiagnostics,
|
|
20
25
|
serializeMdi,
|
|
26
|
+
sourceSpansForTextRange,
|
|
21
27
|
toPublicationMdast
|
|
22
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-JTDOLXFG.js";
|
|
23
29
|
export {
|
|
24
30
|
MDI_IR_VERSION,
|
|
25
31
|
MDI_SPEC_VERSION,
|
|
32
|
+
MDI_TEXT_PROJECTION_VERSION,
|
|
33
|
+
formatMdiTextPosition,
|
|
34
|
+
formatMdiTextRange,
|
|
35
|
+
getMdiTextBlocks,
|
|
26
36
|
initializeMdi,
|
|
27
37
|
parse,
|
|
28
38
|
parseMdiSyntax,
|
|
39
|
+
parseMdiTextPosition,
|
|
29
40
|
prepareRender,
|
|
30
41
|
renderDocx,
|
|
31
42
|
renderDocxWithDiagnostics,
|
|
@@ -40,5 +51,6 @@ export {
|
|
|
40
51
|
renderTextFormatWithDiagnostics,
|
|
41
52
|
renderTextWithDiagnostics,
|
|
42
53
|
serializeMdi,
|
|
54
|
+
sourceSpansForTextRange,
|
|
43
55
|
toPublicationMdast
|
|
44
56
|
};
|
package/dist/node.cjs
CHANGED
|
@@ -41,6 +41,7 @@ module.exports = __toCommonJS(node_exports);
|
|
|
41
41
|
var mdiCore = __toESM(require("@illusions-lab/mdi-core"), 1);
|
|
42
42
|
var import_yaml = require("yaml");
|
|
43
43
|
var {
|
|
44
|
+
getMdiTextBlocksJson,
|
|
44
45
|
parseMdiSyntaxJson,
|
|
45
46
|
renderHtml: renderHtmlFromRust,
|
|
46
47
|
renderEpub: renderEpubFromRust,
|
package/dist/node.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@illusions-lab/mdi",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19",
|
|
4
4
|
"description": "Thin JavaScript binding for the Rust-authoritative MDI parser",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@illusions-lab/mdi-core": "^2.0.
|
|
36
|
-
"@illusions-lab/mdi-export-profile": "^2.0.
|
|
37
|
-
"@illusions-lab/mdi-to-epub": "^2.0.
|
|
35
|
+
"@illusions-lab/mdi-core": "^2.0.19",
|
|
36
|
+
"@illusions-lab/mdi-export-profile": "^2.0.24",
|
|
37
|
+
"@illusions-lab/mdi-to-epub": "^2.0.34",
|
|
38
38
|
"@types/mdast": "^4.0.0",
|
|
39
39
|
"yaml": "^2.0.0"
|
|
40
40
|
},
|