@pre-markdown/parser 0.2.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.
package/dist/index.js ADDED
@@ -0,0 +1,2013 @@
1
+ // src/block/parser.ts
2
+ import {
3
+ createDocument,
4
+ createHeading,
5
+ createParagraph,
6
+ createBlockquote,
7
+ createList,
8
+ createListItem,
9
+ createCodeBlock,
10
+ createThematicBreak,
11
+ createHtmlBlock,
12
+ createTable,
13
+ createTableRow,
14
+ createTableCell,
15
+ createFootnoteDefinition,
16
+ createMathBlock,
17
+ createContainer,
18
+ createDetails,
19
+ createTOC
20
+ } from "@pre-markdown/core";
21
+
22
+ // src/inline/parser.ts
23
+ import {
24
+ createText,
25
+ createEmphasis,
26
+ createStrong,
27
+ createStrikethrough,
28
+ createInlineCode,
29
+ createLink,
30
+ createImage,
31
+ createHtmlInline,
32
+ createBreak,
33
+ createSoftBreak,
34
+ createMathInline,
35
+ createHighlight,
36
+ createSuperscript,
37
+ createSubscript,
38
+ createAutolink,
39
+ createFootnoteReference,
40
+ createFontColor,
41
+ createFontSize,
42
+ createFontBgColor,
43
+ createRuby,
44
+ createEmoji,
45
+ createAudio,
46
+ createVideo,
47
+ createUnderline
48
+ } from "@pre-markdown/core";
49
+ var RE_ESCAPE = /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/;
50
+ var RE_FOOTNOTE_REF = /\[\^([^\]]+)\]/y;
51
+ var RE_AUTOLINK_URI = /<([a-zA-Z][a-zA-Z0-9+.\-]{1,31}:[^\s<>]*)>/y;
52
+ var RE_AUTOLINK_EMAIL = /<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/y;
53
+ var RE_HTML_INLINE_OPEN = /<[a-zA-Z][a-zA-Z0-9-]*(?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*\/?>/y;
54
+ var RE_HTML_INLINE_CLOSE = /<\/[a-zA-Z][a-zA-Z0-9-]*\s*>/y;
55
+ var RE_HTML_COMMENT = /<!--(?!>)(?!->)[\s\S]*?-->/y;
56
+ var RE_HTML_PI = /<\?[\s\S]*?\?>/y;
57
+ var RE_HTML_DECL = /<![A-Z]+[^>]*>/y;
58
+ var RE_HTML_CDATA = /<!\[CDATA\[[\s\S]*?\]\]>/y;
59
+ var RE_CHERRY_COLOR = /!!(#[0-9a-zA-Z]{3,6}|[a-z]{3,20})\s([\w\W]+?)!!/y;
60
+ var RE_CHERRY_SIZE = /!([0-9]{1,2})\s([\w\W]*?)!/y;
61
+ var RE_CHERRY_BGCOLOR = /!!!(#[0-9a-zA-Z]{3,6}|[a-z]{3,10})\s([\w\W]+?)!!!/y;
62
+ var RE_FONT_COLOR = /\{color:([^}]+)\}/y;
63
+ var RE_FONT_SIZE = /\{size:([^}]+)\}/y;
64
+ var RE_FONT_BGCOLOR = /\{bgcolor:([^}]+)\}/y;
65
+ var RE_EMOJI = /:([a-zA-Z0-9_+-]+):/y;
66
+ var RE_AUDIO = /!audio\[([^\]]*)\]\(([^)]+)\)/y;
67
+ var RE_VIDEO = /!video\[([^\]]*)\]\(([^)]+)\)/y;
68
+ var RE_ENTITY = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/y;
69
+ var HTML_ENTITIES = {
70
+ amp: "&",
71
+ lt: "<",
72
+ gt: ">",
73
+ quot: '"',
74
+ apos: "'",
75
+ nbsp: "\xA0",
76
+ copy: "\xA9",
77
+ reg: "\xAE",
78
+ AElig: "\xC6",
79
+ Dcaron: "\u010E",
80
+ frac34: "\xBE",
81
+ HilbertSpace: "\u210B",
82
+ DifferentialD: "\u2146",
83
+ ClockwiseContourIntegral: "\u2232",
84
+ ngE: "\u2267\u0338",
85
+ ouml: "\xF6",
86
+ uuml: "\xFC",
87
+ auml: "\xE4",
88
+ szlig: "\xDF",
89
+ ntilde: "\xF1",
90
+ eacute: "\xE9",
91
+ Ouml: "\xD6",
92
+ Uuml: "\xDC",
93
+ Auml: "\xC4",
94
+ mdash: "\u2014",
95
+ ndash: "\u2013",
96
+ lsquo: "\u2018",
97
+ rsquo: "\u2019",
98
+ ldquo: "\u201C",
99
+ rdquo: "\u201D",
100
+ bull: "\u2022",
101
+ hellip: "\u2026",
102
+ trade: "\u2122",
103
+ larr: "\u2190",
104
+ rarr: "\u2192",
105
+ uarr: "\u2191",
106
+ darr: "\u2193",
107
+ laquo: "\xAB",
108
+ raquo: "\xBB",
109
+ euro: "\u20AC",
110
+ pound: "\xA3",
111
+ yen: "\xA5",
112
+ cent: "\xA2",
113
+ times: "\xD7",
114
+ divide: "\xF7",
115
+ plusmn: "\xB1",
116
+ infin: "\u221E",
117
+ ne: "\u2260",
118
+ le: "\u2264",
119
+ ge: "\u2265",
120
+ deg: "\xB0",
121
+ micro: "\xB5",
122
+ para: "\xB6",
123
+ sect: "\xA7",
124
+ alpha: "\u03B1",
125
+ beta: "\u03B2",
126
+ gamma: "\u03B3",
127
+ delta: "\u03B4",
128
+ epsilon: "\u03B5",
129
+ pi: "\u03C0",
130
+ sigma: "\u03C3",
131
+ omega: "\u03C9",
132
+ hearts: "\u2665",
133
+ diams: "\u2666",
134
+ clubs: "\u2663",
135
+ spades: "\u2660",
136
+ iexcl: "\xA1",
137
+ iquest: "\xBF",
138
+ acute: "\xB4",
139
+ cedil: "\xB8",
140
+ ordf: "\xAA",
141
+ ordm: "\xBA",
142
+ sup1: "\xB9",
143
+ sup2: "\xB2",
144
+ sup3: "\xB3",
145
+ frac12: "\xBD",
146
+ frac14: "\xBC"
147
+ };
148
+ function execAt(re, input, pos) {
149
+ re.lastIndex = pos;
150
+ return re.exec(input);
151
+ }
152
+ function parseInline(input) {
153
+ const nodes = [];
154
+ const len = input.length;
155
+ let pos = 0;
156
+ let textStart = 0;
157
+ function flushText() {
158
+ if (pos > textStart) {
159
+ nodes.push(createText(input.slice(textStart, pos)));
160
+ }
161
+ }
162
+ while (pos < len) {
163
+ const ch = input.charCodeAt(pos);
164
+ if (ch === 10) {
165
+ const prevText = input.slice(textStart, pos);
166
+ if (prevText.endsWith(" ") || prevText.endsWith("\\")) {
167
+ const trimmed = prevText.charCodeAt(prevText.length - 1) === 92 ? prevText.slice(0, -1) : prevText.replace(/ +$/, "");
168
+ if (trimmed) nodes.push(createText(trimmed));
169
+ nodes.push(createBreak());
170
+ } else {
171
+ const trimmedPrev = prevText.replace(/ +$/, "");
172
+ if (trimmedPrev) nodes.push(createText(trimmedPrev));
173
+ else flushText();
174
+ nodes.push(createSoftBreak());
175
+ }
176
+ pos++;
177
+ while (pos < len && input.charCodeAt(pos) === 32) pos++;
178
+ textStart = pos;
179
+ continue;
180
+ }
181
+ if (ch === 92 && pos + 1 < len) {
182
+ const next = input[pos + 1];
183
+ if (RE_ESCAPE.test(next)) {
184
+ flushText();
185
+ pos++;
186
+ textStart = pos;
187
+ pos++;
188
+ continue;
189
+ }
190
+ }
191
+ if (ch === 96) {
192
+ const result = tryInlineCode(input, pos);
193
+ if (result) {
194
+ flushText();
195
+ nodes.push(result.node);
196
+ pos = result.end;
197
+ textStart = pos;
198
+ continue;
199
+ }
200
+ }
201
+ if (ch === 36 && input.charCodeAt(pos + 1) !== 36) {
202
+ const result = tryMathInline(input, pos);
203
+ if (result) {
204
+ flushText();
205
+ nodes.push(result.node);
206
+ pos = result.end;
207
+ textStart = pos;
208
+ continue;
209
+ }
210
+ }
211
+ if (ch === 33 && input.charCodeAt(pos + 1) === 33 && input.charCodeAt(pos + 2) === 33) {
212
+ const match = execAt(RE_CHERRY_BGCOLOR, input, pos);
213
+ if (match) {
214
+ flushText();
215
+ nodes.push(createFontBgColor(match[1], parseInlineFast(match[2])));
216
+ pos = pos + match[0].length;
217
+ textStart = pos;
218
+ continue;
219
+ }
220
+ }
221
+ if (ch === 33 && input.charCodeAt(pos + 1) === 33 && input.charCodeAt(pos + 2) !== 33) {
222
+ const match = execAt(RE_CHERRY_COLOR, input, pos);
223
+ if (match) {
224
+ flushText();
225
+ nodes.push(createFontColor(match[1], parseInlineFast(match[2])));
226
+ pos = pos + match[0].length;
227
+ textStart = pos;
228
+ continue;
229
+ }
230
+ }
231
+ if (ch === 33) {
232
+ const next = input.charCodeAt(pos + 1);
233
+ if (next !== 33 && next !== 91 && next !== 97 && next !== 118) {
234
+ const match = execAt(RE_CHERRY_SIZE, input, pos);
235
+ if (match) {
236
+ flushText();
237
+ nodes.push(createFontSize(match[1] + "px", parseInlineFast(match[2])));
238
+ pos = pos + match[0].length;
239
+ textStart = pos;
240
+ continue;
241
+ }
242
+ }
243
+ }
244
+ if (ch === 33 && input.charCodeAt(pos + 1) === 97) {
245
+ const match = execAt(RE_AUDIO, input, pos);
246
+ if (match) {
247
+ flushText();
248
+ nodes.push(createAudio(match[2], match[1] || void 0));
249
+ pos = pos + match[0].length;
250
+ textStart = pos;
251
+ continue;
252
+ }
253
+ }
254
+ if (ch === 33 && input.charCodeAt(pos + 1) === 118) {
255
+ const match = execAt(RE_VIDEO, input, pos);
256
+ if (match) {
257
+ flushText();
258
+ nodes.push(createVideo(match[2], match[1] || void 0));
259
+ pos = pos + match[0].length;
260
+ textStart = pos;
261
+ continue;
262
+ }
263
+ }
264
+ if (ch === 33 && input.charCodeAt(pos + 1) === 91) {
265
+ const result = tryImage(input, pos);
266
+ if (result) {
267
+ flushText();
268
+ nodes.push(result.node);
269
+ pos = result.end;
270
+ textStart = pos;
271
+ continue;
272
+ }
273
+ }
274
+ if (ch === 91) {
275
+ if (input.charCodeAt(pos + 1) === 94) {
276
+ const match = execAt(RE_FOOTNOTE_REF, input, pos);
277
+ if (match) {
278
+ flushText();
279
+ nodes.push(createFootnoteReference(match[1], match[1]));
280
+ pos = pos + match[0].length;
281
+ textStart = pos;
282
+ continue;
283
+ }
284
+ }
285
+ const result = tryLink(input, pos);
286
+ if (result) {
287
+ flushText();
288
+ nodes.push(result.node);
289
+ pos = result.end;
290
+ textStart = pos;
291
+ continue;
292
+ }
293
+ }
294
+ if (ch === 60) {
295
+ const uriMatch = execAt(RE_AUTOLINK_URI, input, pos);
296
+ if (uriMatch) {
297
+ flushText();
298
+ nodes.push(createAutolink(uriMatch[1], false));
299
+ pos = pos + uriMatch[0].length;
300
+ textStart = pos;
301
+ continue;
302
+ }
303
+ const emailMatch = execAt(RE_AUTOLINK_EMAIL, input, pos);
304
+ if (emailMatch) {
305
+ flushText();
306
+ nodes.push(createAutolink("mailto:" + emailMatch[1], true));
307
+ pos = pos + emailMatch[0].length;
308
+ textStart = pos;
309
+ continue;
310
+ }
311
+ const htmlPatterns = [RE_HTML_INLINE_OPEN, RE_HTML_INLINE_CLOSE, RE_HTML_COMMENT, RE_HTML_PI, RE_HTML_CDATA, RE_HTML_DECL];
312
+ let htmlMatched = false;
313
+ for (let hi = 0; hi < htmlPatterns.length; hi++) {
314
+ const hm = execAt(htmlPatterns[hi], input, pos);
315
+ if (hm) {
316
+ flushText();
317
+ nodes.push(createHtmlInline(hm[0]));
318
+ pos = pos + hm[0].length;
319
+ textStart = pos;
320
+ htmlMatched = true;
321
+ break;
322
+ }
323
+ }
324
+ if (htmlMatched) continue;
325
+ }
326
+ if (ch === 42 || ch === 95) {
327
+ const result = tryEmphasis(input, pos);
328
+ if (result) {
329
+ flushText();
330
+ nodes.push(result.node);
331
+ pos = result.end;
332
+ textStart = pos;
333
+ continue;
334
+ }
335
+ }
336
+ if (ch === 126 && input.charCodeAt(pos + 1) === 126) {
337
+ const result = tryStrikethrough(input, pos);
338
+ if (result) {
339
+ flushText();
340
+ nodes.push(result.node);
341
+ pos = result.end;
342
+ textStart = pos;
343
+ continue;
344
+ }
345
+ }
346
+ if (ch === 61 && input.charCodeAt(pos + 1) === 61) {
347
+ const result = tryHighlight(input, pos);
348
+ if (result) {
349
+ flushText();
350
+ nodes.push(result.node);
351
+ pos = result.end;
352
+ textStart = pos;
353
+ continue;
354
+ }
355
+ }
356
+ if (ch === 94 && input.charCodeAt(pos + 1) !== 94) {
357
+ const result = trySuperscript(input, pos);
358
+ if (result) {
359
+ flushText();
360
+ nodes.push(result.node);
361
+ pos = result.end;
362
+ textStart = pos;
363
+ continue;
364
+ }
365
+ }
366
+ if (ch === 94 && input.charCodeAt(pos + 1) === 94) {
367
+ const result = tryCherrySubscript(input, pos);
368
+ if (result) {
369
+ flushText();
370
+ nodes.push(result.node);
371
+ pos = result.end;
372
+ textStart = pos;
373
+ continue;
374
+ }
375
+ }
376
+ if (ch === 126 && input.charCodeAt(pos + 1) !== 126) {
377
+ const result = trySubscript(input, pos);
378
+ if (result) {
379
+ flushText();
380
+ nodes.push(result.node);
381
+ pos = result.end;
382
+ textStart = pos;
383
+ continue;
384
+ }
385
+ }
386
+ if (ch === 123) {
387
+ const colorMatch = execAt(RE_FONT_COLOR, input, pos);
388
+ if (colorMatch) {
389
+ const closeTag = "{/color}";
390
+ const closeIdx = input.indexOf(closeTag, pos + colorMatch[0].length);
391
+ if (closeIdx !== -1) {
392
+ const content = input.slice(pos + colorMatch[0].length, closeIdx);
393
+ if (content.length > 0) {
394
+ flushText();
395
+ nodes.push(createFontColor(colorMatch[1], parseInlineFast(content)));
396
+ pos = closeIdx + closeTag.length;
397
+ textStart = pos;
398
+ continue;
399
+ }
400
+ }
401
+ }
402
+ const sizeMatch = execAt(RE_FONT_SIZE, input, pos);
403
+ if (sizeMatch) {
404
+ const closeTag = "{/size}";
405
+ const closeIdx = input.indexOf(closeTag, pos + sizeMatch[0].length);
406
+ if (closeIdx !== -1) {
407
+ const content = input.slice(pos + sizeMatch[0].length, closeIdx);
408
+ if (content.length > 0) {
409
+ flushText();
410
+ nodes.push(createFontSize(sizeMatch[1], parseInlineFast(content)));
411
+ pos = closeIdx + closeTag.length;
412
+ textStart = pos;
413
+ continue;
414
+ }
415
+ }
416
+ }
417
+ const bgMatch = execAt(RE_FONT_BGCOLOR, input, pos);
418
+ if (bgMatch) {
419
+ const closeTag = "{/bgcolor}";
420
+ const closeIdx = input.indexOf(closeTag, pos + bgMatch[0].length);
421
+ if (closeIdx !== -1) {
422
+ const content = input.slice(pos + bgMatch[0].length, closeIdx);
423
+ if (content.length > 0) {
424
+ flushText();
425
+ nodes.push(createFontBgColor(bgMatch[1], parseInlineFast(content)));
426
+ pos = closeIdx + closeTag.length;
427
+ textStart = pos;
428
+ continue;
429
+ }
430
+ }
431
+ }
432
+ const result = tryRuby(input, pos);
433
+ if (result) {
434
+ flushText();
435
+ nodes.push(result.node);
436
+ pos = result.end;
437
+ textStart = pos;
438
+ continue;
439
+ }
440
+ }
441
+ if (ch === 47) {
442
+ const result = tryUnderline(input, pos);
443
+ if (result) {
444
+ flushText();
445
+ nodes.push(result.node);
446
+ pos = result.end;
447
+ textStart = pos;
448
+ continue;
449
+ }
450
+ }
451
+ if (ch === 58) {
452
+ const match = execAt(RE_EMOJI, input, pos);
453
+ if (match) {
454
+ const value = EMOJI_MAP[match[1]];
455
+ if (value) {
456
+ flushText();
457
+ nodes.push(createEmoji(match[1], value));
458
+ pos = pos + match[0].length;
459
+ textStart = pos;
460
+ continue;
461
+ }
462
+ }
463
+ }
464
+ if (ch === 38) {
465
+ const match = execAt(RE_ENTITY, input, pos);
466
+ if (match) {
467
+ let decoded;
468
+ if (match[1]) {
469
+ const code = parseInt(match[1], 16);
470
+ decoded = code === 0 ? "\uFFFD" : String.fromCodePoint(code);
471
+ } else if (match[2]) {
472
+ const code = parseInt(match[2], 10);
473
+ decoded = code === 0 ? "\uFFFD" : String.fromCodePoint(code);
474
+ } else if (match[3]) {
475
+ decoded = HTML_ENTITIES[match[3]];
476
+ }
477
+ if (decoded !== void 0) {
478
+ flushText();
479
+ nodes.push(createText(decoded));
480
+ pos = pos + match[0].length;
481
+ textStart = pos;
482
+ continue;
483
+ }
484
+ }
485
+ }
486
+ pos++;
487
+ }
488
+ if (textStart < len) {
489
+ nodes.push(createText(input.slice(textStart)));
490
+ }
491
+ return nodes;
492
+ }
493
+ var INLINE_SPECIAL_CHARS = /[`*_~\[!<$\\={^:/{}\n]/;
494
+ function parseInlineFast(content) {
495
+ if (!INLINE_SPECIAL_CHARS.test(content)) {
496
+ return content.length > 0 ? [createText(content)] : [];
497
+ }
498
+ return parseInline(content);
499
+ }
500
+ function tryInlineCode(input, start) {
501
+ let pos = start;
502
+ let ticks = 0;
503
+ while (pos < input.length && input.charCodeAt(pos) === 96) {
504
+ ticks++;
505
+ pos++;
506
+ }
507
+ let searchPos = pos;
508
+ while (searchPos < input.length) {
509
+ while (searchPos < input.length && input.charCodeAt(searchPos) !== 96) searchPos++;
510
+ if (searchPos >= input.length) return null;
511
+ let runStart = searchPos;
512
+ while (searchPos < input.length && input.charCodeAt(searchPos) === 96) searchPos++;
513
+ const runLen = searchPos - runStart;
514
+ if (runLen === ticks) {
515
+ const content = input.slice(pos, runStart);
516
+ let trimmed = content;
517
+ if (content.includes("\n")) trimmed = content.replace(/\n/g, " ");
518
+ const normalized = trimmed.length > 0 && trimmed.charCodeAt(0) === 32 && trimmed.charCodeAt(trimmed.length - 1) === 32 && trimmed.trim().length > 0 ? trimmed.slice(1, -1) : trimmed;
519
+ return {
520
+ node: createInlineCode(normalized),
521
+ end: runStart + ticks
522
+ };
523
+ }
524
+ }
525
+ return null;
526
+ }
527
+ function tryMathInline(input, start) {
528
+ const pos = start + 1;
529
+ const closePos = input.indexOf("$", pos);
530
+ if (closePos === -1 || closePos === pos) return null;
531
+ if (input.charCodeAt(pos) === 32 || input.charCodeAt(closePos - 1) === 32) return null;
532
+ return {
533
+ node: createMathInline(input.slice(pos, closePos)),
534
+ end: closePos + 1
535
+ };
536
+ }
537
+ function tryImage(input, start) {
538
+ const altClose = findClosingBracket(input, start + 1);
539
+ if (altClose === -1 || input.charCodeAt(altClose + 1) !== 40) return null;
540
+ const alt = input.slice(start + 2, altClose);
541
+ const urlResult = parseUrlAndTitle(input, altClose + 1);
542
+ if (!urlResult) return null;
543
+ return {
544
+ node: createImage(urlResult.url, alt, urlResult.title),
545
+ end: urlResult.end
546
+ };
547
+ }
548
+ function tryLink(input, start) {
549
+ const textClose = findClosingBracket(input, start);
550
+ if (textClose === -1 || input.charCodeAt(textClose + 1) !== 40) return null;
551
+ const text = input.slice(start + 1, textClose);
552
+ const urlResult = parseUrlAndTitle(input, textClose + 1);
553
+ if (!urlResult) return null;
554
+ return {
555
+ node: createLink(urlResult.url, parseInlineFast(text), urlResult.title),
556
+ end: urlResult.end
557
+ };
558
+ }
559
+ function tryEmphasis(input, start) {
560
+ const charCode = input.charCodeAt(start);
561
+ let count = 1;
562
+ while (start + count < input.length && input.charCodeAt(start + count) === charCode) count++;
563
+ if (count > 3) count = 3;
564
+ const pos = start + count;
565
+ let searchPos = pos;
566
+ while (searchPos < input.length) {
567
+ let closeIdx = -1;
568
+ for (let k = searchPos; k < input.length; k++) {
569
+ if (input.charCodeAt(k) === charCode) {
570
+ closeIdx = k;
571
+ break;
572
+ }
573
+ }
574
+ if (closeIdx === -1) return null;
575
+ let closeCount = 1;
576
+ while (closeIdx + closeCount < input.length && input.charCodeAt(closeIdx + closeCount) === charCode) closeCount++;
577
+ if (closeCount < count) {
578
+ searchPos = closeIdx + closeCount;
579
+ continue;
580
+ }
581
+ const actualCloseStart = closeIdx + closeCount - count;
582
+ const content = input.slice(pos, actualCloseStart);
583
+ if (content.length === 0) {
584
+ searchPos = closeIdx + closeCount;
585
+ continue;
586
+ }
587
+ const children = parseInlineFast(content);
588
+ const end = actualCloseStart + count;
589
+ if (count === 1) {
590
+ return { node: createEmphasis(children), end };
591
+ } else if (count === 2) {
592
+ return { node: createStrong(children), end };
593
+ } else {
594
+ return { node: createStrong([createEmphasis(children)]), end };
595
+ }
596
+ }
597
+ return null;
598
+ }
599
+ function tryStrikethrough(input, start) {
600
+ for (let k = start + 2; k < input.length - 1; k++) {
601
+ if (input.charCodeAt(k) === 126 && input.charCodeAt(k + 1) === 126) {
602
+ const content = input.slice(start + 2, k);
603
+ if (content.length === 0) return null;
604
+ return { node: createStrikethrough(parseInlineFast(content)), end: k + 2 };
605
+ }
606
+ }
607
+ return null;
608
+ }
609
+ function tryHighlight(input, start) {
610
+ for (let k = start + 2; k < input.length - 1; k++) {
611
+ if (input.charCodeAt(k) === 61 && input.charCodeAt(k + 1) === 61) {
612
+ const content = input.slice(start + 2, k);
613
+ if (content.length === 0) return null;
614
+ return { node: createHighlight(parseInlineFast(content)), end: k + 2 };
615
+ }
616
+ }
617
+ return null;
618
+ }
619
+ function trySuperscript(input, start) {
620
+ const closeIdx = input.indexOf("^", start + 1);
621
+ if (closeIdx === -1 || closeIdx === start + 1) return null;
622
+ const content = input.slice(start + 1, closeIdx);
623
+ if (content.includes(" ")) return null;
624
+ return { node: createSuperscript(parseInlineFast(content)), end: closeIdx + 1 };
625
+ }
626
+ function trySubscript(input, start) {
627
+ if (input.charCodeAt(start + 1) === 126) return null;
628
+ const closeIdx = input.indexOf("~", start + 1);
629
+ if (closeIdx === -1 || closeIdx === start + 1) return null;
630
+ const content = input.slice(start + 1, closeIdx);
631
+ if (content.includes(" ")) return null;
632
+ return { node: createSubscript(parseInlineFast(content)), end: closeIdx + 1 };
633
+ }
634
+ function tryCherrySubscript(input, start) {
635
+ const closeIdx = input.indexOf("^^", start + 2);
636
+ if (closeIdx === -1) return null;
637
+ const content = input.slice(start + 2, closeIdx);
638
+ if (content.length === 0) return null;
639
+ return { node: createSubscript(parseInlineFast(content)), end: closeIdx + 2 };
640
+ }
641
+ function findClosingBracket(input, start) {
642
+ let depth = 0;
643
+ let pos = start;
644
+ while (pos < input.length) {
645
+ const c = input.charCodeAt(pos);
646
+ if (c === 91) depth++;
647
+ else if (c === 93) {
648
+ depth--;
649
+ if (depth === 0) return pos;
650
+ } else if (c === 92) {
651
+ pos++;
652
+ }
653
+ pos++;
654
+ }
655
+ return -1;
656
+ }
657
+ function parseUrlAndTitle(input, start) {
658
+ if (input.charCodeAt(start) !== 40) return null;
659
+ let pos = start + 1;
660
+ while (pos < input.length && isWhitespace(input.charCodeAt(pos))) pos++;
661
+ let url = "";
662
+ if (input.charCodeAt(pos) === 60) {
663
+ const closeAngle = input.indexOf(">", pos + 1);
664
+ if (closeAngle === -1) return null;
665
+ url = input.slice(pos + 1, closeAngle);
666
+ pos = closeAngle + 1;
667
+ } else {
668
+ let depth = 0;
669
+ const urlStart = pos;
670
+ while (pos < input.length) {
671
+ const c = input.charCodeAt(pos);
672
+ if (c === 40) depth++;
673
+ else if (c === 41) {
674
+ if (depth === 0) break;
675
+ depth--;
676
+ } else if (isWhitespace(c)) break;
677
+ pos++;
678
+ }
679
+ url = input.slice(urlStart, pos);
680
+ }
681
+ while (pos < input.length && isWhitespace(input.charCodeAt(pos))) pos++;
682
+ let title;
683
+ const tc = input.charCodeAt(pos);
684
+ if (tc === 34 || tc === 39 || tc === 40) {
685
+ const quote = tc === 40 ? 41 : tc;
686
+ const titleStart = pos + 1;
687
+ pos++;
688
+ while (pos < input.length && input.charCodeAt(pos) !== quote) {
689
+ if (input.charCodeAt(pos) === 92) pos++;
690
+ pos++;
691
+ }
692
+ if (pos >= input.length) return null;
693
+ title = input.slice(titleStart, pos);
694
+ pos++;
695
+ }
696
+ while (pos < input.length && isWhitespace(input.charCodeAt(pos))) pos++;
697
+ if (pos >= input.length || input.charCodeAt(pos) !== 41) return null;
698
+ return { url: decodeEntities(decodeBackslashEscapes(url)), title: title ? decodeEntities(decodeBackslashEscapes(title)) : title, end: pos + 1 };
699
+ }
700
+ function isWhitespace(code) {
701
+ return code === 32 || code === 9 || code === 10 || code === 13;
702
+ }
703
+ var RE_BACKSLASH_ESCAPE = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
704
+ function decodeBackslashEscapes(str) {
705
+ return str.replace(RE_BACKSLASH_ESCAPE, "$1");
706
+ }
707
+ function decodeEntities(str) {
708
+ if (!str.includes("&")) return str;
709
+ return str.replace(/&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g, (match, hex, dec, name) => {
710
+ if (hex) {
711
+ const code = parseInt(hex, 16);
712
+ return code === 0 ? "\uFFFD" : String.fromCodePoint(code);
713
+ }
714
+ if (dec) {
715
+ const code = parseInt(dec, 10);
716
+ return code === 0 ? "\uFFFD" : String.fromCodePoint(code);
717
+ }
718
+ if (name && HTML_ENTITIES[name]) {
719
+ return HTML_ENTITIES[name];
720
+ }
721
+ return match;
722
+ });
723
+ }
724
+ function tryRuby(input, start) {
725
+ const braceClose = input.indexOf("}", start + 1);
726
+ if (braceClose === -1) return null;
727
+ const innerContent = input.slice(start + 1, braceClose);
728
+ if (innerContent.length === 0) return null;
729
+ if (innerContent.charCodeAt(0) === 99 && innerContent.startsWith("color:")) return null;
730
+ if (innerContent.charCodeAt(0) === 115 && innerContent.startsWith("size:")) return null;
731
+ if (innerContent.charCodeAt(0) === 98 && innerContent.startsWith("bgcolor:")) return null;
732
+ if (innerContent.charCodeAt(0) === 47) return null;
733
+ const pipeIdx = innerContent.indexOf("|");
734
+ if (pipeIdx !== -1) {
735
+ const base = innerContent.slice(0, pipeIdx);
736
+ const annotation2 = innerContent.slice(pipeIdx + 1);
737
+ if (base.length > 0 && annotation2.length > 0) {
738
+ return { node: createRuby(base, annotation2), end: braceClose + 1 };
739
+ }
740
+ }
741
+ if (input.charCodeAt(braceClose + 1) !== 40) return null;
742
+ const parenClose = input.indexOf(")", braceClose + 2);
743
+ if (parenClose === -1) return null;
744
+ const annotation = input.slice(braceClose + 2, parenClose);
745
+ if (annotation.length === 0) return null;
746
+ return { node: createRuby(innerContent, annotation), end: parenClose + 1 };
747
+ }
748
+ function tryUnderline(input, start) {
749
+ if (start > 0 && input.charCodeAt(start - 1) !== 32 && input.charCodeAt(start - 1) !== 10) return null;
750
+ const closeIdx = input.indexOf("/", start + 1);
751
+ if (closeIdx === -1 || closeIdx === start + 1) return null;
752
+ const content = input.slice(start + 1, closeIdx);
753
+ if (content.includes("\n")) return null;
754
+ if (closeIdx + 1 < input.length && input.charCodeAt(closeIdx + 1) !== 32 && input.charCodeAt(closeIdx + 1) !== 10) return null;
755
+ return { node: createUnderline(parseInlineFast(content)), end: closeIdx + 1 };
756
+ }
757
+ var EMOJI_MAP = {
758
+ smile: "\u{1F604}",
759
+ laughing: "\u{1F606}",
760
+ blush: "\u{1F60A}",
761
+ smiley: "\u{1F603}",
762
+ relaxed: "\u263A\uFE0F",
763
+ heart: "\u2764\uFE0F",
764
+ "thumbsup": "\u{1F44D}",
765
+ "thumbsdown": "\u{1F44E}",
766
+ ok_hand: "\u{1F44C}",
767
+ wave: "\u{1F44B}",
768
+ clap: "\u{1F44F}",
769
+ raised_hands: "\u{1F64C}",
770
+ pray: "\u{1F64F}",
771
+ fire: "\u{1F525}",
772
+ star: "\u2B50",
773
+ sparkles: "\u2728",
774
+ zap: "\u26A1",
775
+ warning: "\u26A0\uFE0F",
776
+ x: "\u274C",
777
+ white_check_mark: "\u2705",
778
+ question: "\u2753",
779
+ exclamation: "\u2757",
780
+ bulb: "\u{1F4A1}",
781
+ memo: "\u{1F4DD}",
782
+ book: "\u{1F4D6}",
783
+ rocket: "\u{1F680}",
784
+ tada: "\u{1F389}",
785
+ eyes: "\u{1F440}",
786
+ thinking: "\u{1F914}",
787
+ sob: "\u{1F62D}",
788
+ joy: "\u{1F602}",
789
+ wink: "\u{1F609}",
790
+ grin: "\u{1F601}",
791
+ sweat_smile: "\u{1F605}",
792
+ sunglasses: "\u{1F60E}",
793
+ heart_eyes: "\u{1F60D}",
794
+ 100: "\u{1F4AF}",
795
+ boom: "\u{1F4A5}",
796
+ muscle: "\u{1F4AA}",
797
+ point_up: "\u261D\uFE0F",
798
+ point_down: "\u{1F447}",
799
+ point_left: "\u{1F448}",
800
+ point_right: "\u{1F449}",
801
+ see_no_evil: "\u{1F648}",
802
+ hear_no_evil: "\u{1F649}",
803
+ speak_no_evil: "\u{1F64A}",
804
+ coffee: "\u2615",
805
+ beer: "\u{1F37A}",
806
+ pizza: "\u{1F355}",
807
+ hamburger: "\u{1F354}",
808
+ dog: "\u{1F436}",
809
+ cat: "\u{1F431}",
810
+ bug: "\u{1F41B}",
811
+ penguin: "\u{1F427}",
812
+ checkered_flag: "\u{1F3C1}",
813
+ trophy: "\u{1F3C6}",
814
+ gem: "\u{1F48E}",
815
+ wrench: "\u{1F527}",
816
+ hammer: "\u{1F528}",
817
+ gear: "\u2699\uFE0F",
818
+ link: "\u{1F517}",
819
+ lock: "\u{1F512}",
820
+ key: "\u{1F511}",
821
+ bell: "\u{1F514}",
822
+ calendar: "\u{1F4C5}",
823
+ clock: "\u{1F550}",
824
+ earth_americas: "\u{1F30E}",
825
+ sunny: "\u2600\uFE0F",
826
+ cloud: "\u2601\uFE0F",
827
+ umbrella: "\u2602\uFE0F",
828
+ snowflake: "\u2744\uFE0F",
829
+ rainbow: "\u{1F308}",
830
+ ocean: "\u{1F30A}",
831
+ "+1": "\u{1F44D}",
832
+ "-1": "\u{1F44E}"
833
+ };
834
+
835
+ // src/block/parser.ts
836
+ var DEFAULT_OPTIONS = {
837
+ gfmTables: true,
838
+ mathBlocks: true,
839
+ containers: true,
840
+ toc: true,
841
+ footnotes: true,
842
+ lazyInline: false
843
+ };
844
+ var RE_ATX_HEADING = /^(#{1,6})(?:\s|$)/;
845
+ var RE_SETEXT_H1 = /^ {0,3}={1,}[ \t]*$/;
846
+ var RE_SETEXT_H2 = /^ {0,3}-{1,}[ \t]*$/;
847
+ var RE_THEMATIC_BREAK = /^(?:\*[ \t]*){3,}$|^(?:-[ \t]*){3,}$|^(?:_[ \t]*){3,}$/;
848
+ var RE_UL_MARKER = /^([*+-])(\s+|$)/;
849
+ var RE_OL_MARKER = /^(\d{1,9})([.)])(\s+|$)/;
850
+ var RE_BLOCKQUOTE = /^>[ \t]?/;
851
+ var RE_FENCE_OPEN = /^(`{3,}|~{3,})(?:\s*(\S+)?.*)?$/;
852
+ var RE_MATH_OPEN = /^\${2}\s*$/;
853
+ var RE_MATH_CLOSE = /^\${2}\s*$/;
854
+ var RE_CONTAINER_OPEN = /^:::[ \t]*(\w+)(?:[ \t]+(.*))?$/;
855
+ var RE_CONTAINER_CLOSE = /^:::[ \t]*$/;
856
+ var RE_TOC = /^(?:\[toc\]|\[\[toc\]\]|【【toc】】)$/i;
857
+ var RE_TABLE_DELIM = /^\|?(?:\s*:?-+:?\s*\|)+\s*:?-+:?\s*\|?\s*$/;
858
+ var RE_HTML_BLOCK_1 = /^<(?:script|pre|style|textarea)(?:\s|>|$)/i;
859
+ var RE_HTML_BLOCK_2 = /^<!--/;
860
+ var RE_HTML_BLOCK_3 = /^<\?/;
861
+ var RE_HTML_BLOCK_4 = /^<![A-Z]/;
862
+ var RE_HTML_BLOCK_5 = /^<!\[CDATA\[/;
863
+ var RE_HTML_BLOCK_6 = /^<\/?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:\s|\/?>|$)/i;
864
+ var RE_HTML_BLOCK_7_OPEN = /^<[a-zA-Z][a-zA-Z0-9-]*(?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*\/?>[ \t]*$/;
865
+ var RE_HTML_BLOCK_7_CLOSE = /^<\/[a-zA-Z][a-zA-Z0-9-]*\s*>[ \t]*$/;
866
+ var RE_TASK = /^\[([ xX])\]\s+/;
867
+ var RE_FOOTNOTE_DEF = /^\[\^([^\]]+)\]:\s+(.*)/;
868
+ var RE_DETAIL_OPEN = /^\+\+\+([-]?)\s+(.+)$/;
869
+ var RE_DETAIL_CLOSE = /^\+\+\+\s*$/;
870
+ var RE_FRONTMATTER_OPEN = /^-{3,}\s*$/;
871
+ function isIndentCode(s) {
872
+ const c = s.charCodeAt(0);
873
+ if (c === 9) return true;
874
+ return c === 32 && s.charCodeAt(1) === 32 && s.charCodeAt(2) === 32 && s.charCodeAt(3) === 32;
875
+ }
876
+ function isBlank(s) {
877
+ for (let i = 0; i < s.length; i++) {
878
+ const c = s.charCodeAt(i);
879
+ if (c !== 32 && c !== 9) return false;
880
+ }
881
+ return true;
882
+ }
883
+ function strip3(s) {
884
+ const c0 = s.charCodeAt(0);
885
+ if (c0 !== 32) return s;
886
+ const c1 = s.charCodeAt(1);
887
+ if (c1 !== 32) return s.slice(1);
888
+ const c2 = s.charCodeAt(2);
889
+ if (c2 !== 32) return s.slice(2);
890
+ return s.slice(3);
891
+ }
892
+ function parseBlocks(input, options) {
893
+ const opts = { ...DEFAULT_OPTIONS, ...options };
894
+ const lines = input.split("\n");
895
+ const children = parseBlockLines(lines, 0, lines.length, opts);
896
+ return createDocument(children);
897
+ }
898
+ function parseBlockLines(lines, start, end, opts) {
899
+ const blocks = [];
900
+ let i = start;
901
+ while (i < end) {
902
+ const line = lines[i];
903
+ if (isBlank(line)) {
904
+ i++;
905
+ continue;
906
+ }
907
+ let result;
908
+ const firstChar = line.charCodeAt(0);
909
+ let trimStart = -1;
910
+ for (let k = 0; k < line.length; k++) {
911
+ const c = line.charCodeAt(k);
912
+ if (c !== 32 && c !== 9) {
913
+ trimStart = k;
914
+ break;
915
+ }
916
+ }
917
+ const fc = trimStart >= 0 ? line.charCodeAt(trimStart) : firstChar;
918
+ if (blocks.length === 0 && i === start && fc === 45) {
919
+ result = tryFrontMatter(lines, i, end);
920
+ if (result) {
921
+ blocks.push(result.node);
922
+ i = result.nextLine;
923
+ continue;
924
+ }
925
+ }
926
+ if (fc === 35 || firstChar === 32 && trimStart <= 3 && trimStart >= 0 && line.charCodeAt(trimStart) === 35) {
927
+ result = tryATXHeading(lines, i, opts);
928
+ if (result) {
929
+ blocks.push(result.node);
930
+ i = result.nextLine;
931
+ continue;
932
+ }
933
+ }
934
+ if (fc === 42 || fc === 45 || fc === 95) {
935
+ result = tryThematicBreak(lines, i);
936
+ if (result) {
937
+ blocks.push(result.node);
938
+ i = result.nextLine;
939
+ continue;
940
+ }
941
+ }
942
+ if (fc === 96 || fc === 126) {
943
+ result = tryFencedCode(lines, i, end);
944
+ if (result) {
945
+ blocks.push(result.node);
946
+ i = result.nextLine;
947
+ continue;
948
+ }
949
+ }
950
+ if (opts.mathBlocks && fc === 36) {
951
+ result = tryMathBlock(lines, i, end);
952
+ if (result) {
953
+ blocks.push(result.node);
954
+ i = result.nextLine;
955
+ continue;
956
+ }
957
+ }
958
+ if (opts.toc && (fc === 91 || fc === 12304)) {
959
+ result = tryTOC(lines, i);
960
+ if (result) {
961
+ blocks.push(result.node);
962
+ i = result.nextLine;
963
+ continue;
964
+ }
965
+ }
966
+ if (opts.footnotes && fc === 91) {
967
+ result = tryFootnoteDefinition(lines, i, end, opts);
968
+ if (result) {
969
+ blocks.push(result.node);
970
+ i = result.nextLine;
971
+ continue;
972
+ }
973
+ }
974
+ if (opts.containers && fc === 58) {
975
+ result = tryContainer(lines, i, end, opts);
976
+ if (result) {
977
+ blocks.push(result.node);
978
+ i = result.nextLine;
979
+ continue;
980
+ }
981
+ }
982
+ if (fc === 43) {
983
+ result = tryDetail(lines, i, end, opts);
984
+ if (result) {
985
+ blocks.push(result.node);
986
+ i = result.nextLine;
987
+ continue;
988
+ }
989
+ }
990
+ if (fc === 62) {
991
+ result = tryBlockquote(lines, i, end, opts);
992
+ if (result) {
993
+ blocks.push(result.node);
994
+ i = result.nextLine;
995
+ continue;
996
+ }
997
+ }
998
+ if (fc === 45 || fc === 42 || fc === 43 || fc >= 48 && fc <= 57) {
999
+ result = tryList(lines, i, end, opts);
1000
+ if (result) {
1001
+ blocks.push(result.node);
1002
+ i = result.nextLine;
1003
+ continue;
1004
+ }
1005
+ }
1006
+ if (fc === 60) {
1007
+ result = tryHtmlBlock(lines, i, end);
1008
+ if (result) {
1009
+ blocks.push(result.node);
1010
+ i = result.nextLine;
1011
+ continue;
1012
+ }
1013
+ }
1014
+ if (opts.gfmTables && line.includes("|")) {
1015
+ result = tryTable(lines, i, end, opts);
1016
+ if (result) {
1017
+ blocks.push(result.node);
1018
+ i = result.nextLine;
1019
+ continue;
1020
+ }
1021
+ }
1022
+ if (firstChar === 32 || firstChar === 9) {
1023
+ result = tryIndentedCode(lines, i, end);
1024
+ if (result) {
1025
+ blocks.push(result.node);
1026
+ i = result.nextLine;
1027
+ continue;
1028
+ }
1029
+ }
1030
+ result = tryParagraphOrSetext(lines, i, end, opts);
1031
+ if (result) {
1032
+ blocks.push(result.node);
1033
+ i = result.nextLine;
1034
+ continue;
1035
+ }
1036
+ i++;
1037
+ }
1038
+ return blocks;
1039
+ }
1040
+ function inlineOrLazy(content, opts) {
1041
+ if (opts.lazyInline) return [];
1042
+ return parseInline(content);
1043
+ }
1044
+ function tryATXHeading(lines, i, _opts) {
1045
+ let line = lines[i];
1046
+ const stripped = strip3(line);
1047
+ const match = RE_ATX_HEADING.exec(stripped);
1048
+ if (!match) return null;
1049
+ const depth = match[1].length;
1050
+ let content = stripped.slice(match[0].length);
1051
+ content = content.replace(/\s+#+\s*$/, "");
1052
+ if (/^#+\s*$/.test(content)) content = "";
1053
+ content = content.trim();
1054
+ const children = content ? inlineOrLazy(content, _opts) : [];
1055
+ const node = createHeading(depth, children);
1056
+ if (_opts.lazyInline && content) node._raw = content;
1057
+ return {
1058
+ node,
1059
+ nextLine: i + 1
1060
+ };
1061
+ }
1062
+ function tryThematicBreak(lines, i) {
1063
+ const line = lines[i];
1064
+ const stripped = strip3(line);
1065
+ if (!RE_THEMATIC_BREAK.test(stripped)) return null;
1066
+ return {
1067
+ node: createThematicBreak(),
1068
+ nextLine: i + 1
1069
+ };
1070
+ }
1071
+ function tryFencedCode(lines, i, end) {
1072
+ const line = lines[i];
1073
+ let indent = 0;
1074
+ while (indent < 3 && indent < line.length && line.charCodeAt(indent) === 32) indent++;
1075
+ const stripped = line.slice(indent);
1076
+ const openMatch = RE_FENCE_OPEN.exec(stripped);
1077
+ if (!openMatch) return null;
1078
+ const fence = openMatch[1];
1079
+ const rawLang = openMatch[2] ?? void 0;
1080
+ const isBacktick = fence[0] === "`";
1081
+ const fenceLen = fence.length;
1082
+ if (isBacktick) {
1083
+ const afterFence = stripped.slice(fenceLen);
1084
+ if (afterFence.includes("`")) return null;
1085
+ }
1086
+ const lang = rawLang ? decodeInfoString(rawLang) : void 0;
1087
+ const contentLines = [];
1088
+ let j = i + 1;
1089
+ while (j < end) {
1090
+ const current = lines[j];
1091
+ const closeStripped = strip3(current);
1092
+ if (isBacktick && /^`{3,}\s*$/.test(closeStripped) && closeStripped.trim().length >= fenceLen) {
1093
+ j++;
1094
+ break;
1095
+ }
1096
+ if (!isBacktick && /^~{3,}\s*$/.test(closeStripped) && closeStripped.trim().length >= fenceLen) {
1097
+ j++;
1098
+ break;
1099
+ }
1100
+ if (indent > 0 && current.length > 0) {
1101
+ let strip = 0;
1102
+ for (let k = 0; k < indent && k < current.length && current[k] === " "; k++) strip++;
1103
+ contentLines.push(current.slice(strip));
1104
+ } else {
1105
+ contentLines.push(current);
1106
+ }
1107
+ j++;
1108
+ }
1109
+ return {
1110
+ node: createCodeBlock(contentLines.join("\n"), lang),
1111
+ nextLine: j
1112
+ };
1113
+ }
1114
+ function tryMathBlock(lines, i, end) {
1115
+ const line = lines[i];
1116
+ if (!RE_MATH_OPEN.test(line)) return null;
1117
+ const contentLines = [];
1118
+ let j = i + 1;
1119
+ while (j < end) {
1120
+ if (RE_MATH_CLOSE.test(lines[j])) {
1121
+ j++;
1122
+ break;
1123
+ }
1124
+ contentLines.push(lines[j]);
1125
+ j++;
1126
+ }
1127
+ return {
1128
+ node: createMathBlock(contentLines.join("\n")),
1129
+ nextLine: j
1130
+ };
1131
+ }
1132
+ function tryTOC(lines, i) {
1133
+ const line = lines[i].trim();
1134
+ if (!RE_TOC.test(line)) return null;
1135
+ return {
1136
+ node: createTOC(),
1137
+ nextLine: i + 1
1138
+ };
1139
+ }
1140
+ function expandContainerKind(raw) {
1141
+ switch (raw.toLowerCase()) {
1142
+ case "p":
1143
+ return "primary";
1144
+ case "i":
1145
+ return "info";
1146
+ case "w":
1147
+ return "warning";
1148
+ case "d":
1149
+ return "danger";
1150
+ case "s":
1151
+ return "success";
1152
+ case "l":
1153
+ return "left";
1154
+ case "c":
1155
+ return "center";
1156
+ case "r":
1157
+ return "right";
1158
+ case "j":
1159
+ return "justify";
1160
+ case "tip":
1161
+ return "info";
1162
+ default:
1163
+ return raw;
1164
+ }
1165
+ }
1166
+ function tryContainer(lines, i, end, opts) {
1167
+ const line = lines[i];
1168
+ const openMatch = RE_CONTAINER_OPEN.exec(line);
1169
+ if (!openMatch) return null;
1170
+ const rawKind = openMatch[1];
1171
+ const title = openMatch[2] ?? void 0;
1172
+ const kind = expandContainerKind(rawKind);
1173
+ const contentLines = [];
1174
+ let j = i + 1;
1175
+ while (j < end) {
1176
+ if (RE_CONTAINER_CLOSE.test(lines[j])) {
1177
+ j++;
1178
+ break;
1179
+ }
1180
+ contentLines.push(lines[j]);
1181
+ j++;
1182
+ }
1183
+ const children = parseBlockLines(contentLines, 0, contentLines.length, opts);
1184
+ return {
1185
+ node: createContainer(kind, children, title),
1186
+ nextLine: j
1187
+ };
1188
+ }
1189
+ function tryBlockquote(lines, i, end, opts) {
1190
+ const line = lines[i];
1191
+ const stripped = strip3(line);
1192
+ if (!RE_BLOCKQUOTE.test(stripped)) return null;
1193
+ const contentLines = [];
1194
+ let j = i;
1195
+ while (j < end) {
1196
+ const current = lines[j];
1197
+ const curStripped = strip3(current);
1198
+ if (RE_BLOCKQUOTE.test(curStripped)) {
1199
+ contentLines.push(curStripped.replace(RE_BLOCKQUOTE, ""));
1200
+ j++;
1201
+ } else if (!isBlank(current) && contentLines.length > 0 && // Don't lazily continue after a blank content line (empty > line)
1202
+ contentLines[contentLines.length - 1].trim().length > 0 && // Don't lazily continue if the line starts a new block element
1203
+ // (reuse curStripped instead of re-computing strip3)
1204
+ !RE_ATX_HEADING.test(curStripped) && !RE_THEMATIC_BREAK.test(curStripped) && !RE_FENCE_OPEN.test(curStripped) && !RE_UL_MARKER.test(curStripped) && !RE_OL_MARKER.test(curStripped) && !RE_HTML_BLOCK_1.test(current) && !RE_HTML_BLOCK_6.test(current) && // Only lazily continue paragraphs, not code blocks or other blocks
1205
+ // Check that last content line is not inside an open fenced code block
1206
+ !isInsideOpenFencedCode(contentLines) && // Don't lazily continue if blockquote content ends with indented code
1207
+ // (i.e., last non-blank content line is indented code)
1208
+ !lastContentIsIndentedCode(contentLines)) {
1209
+ contentLines.push(current);
1210
+ j++;
1211
+ } else {
1212
+ break;
1213
+ }
1214
+ }
1215
+ const children = parseBlockLines(contentLines, 0, contentLines.length, opts);
1216
+ return {
1217
+ node: createBlockquote(children),
1218
+ nextLine: j
1219
+ };
1220
+ }
1221
+ function isInsideOpenFencedCode(contentLines) {
1222
+ let insideFence = false;
1223
+ let fenceChar = "";
1224
+ let fenceLen = 0;
1225
+ for (const line of contentLines) {
1226
+ const stripped = strip3(line);
1227
+ if (!insideFence) {
1228
+ const openMatch = RE_FENCE_OPEN.exec(stripped);
1229
+ if (openMatch) {
1230
+ insideFence = true;
1231
+ fenceChar = openMatch[1][0];
1232
+ fenceLen = openMatch[1].length;
1233
+ }
1234
+ } else {
1235
+ const closeStripped = stripped;
1236
+ if (fenceChar === "`" && /^`{3,}\s*$/.test(closeStripped) && closeStripped.trim().length >= fenceLen) {
1237
+ insideFence = false;
1238
+ } else if (fenceChar === "~" && /^~{3,}\s*$/.test(closeStripped) && closeStripped.trim().length >= fenceLen) {
1239
+ insideFence = false;
1240
+ }
1241
+ }
1242
+ }
1243
+ return insideFence;
1244
+ }
1245
+ function lastContentIsIndentedCode(contentLines) {
1246
+ let idx = contentLines.length - 1;
1247
+ while (idx >= 0 && isBlank(contentLines[idx])) {
1248
+ idx--;
1249
+ }
1250
+ if (idx < 0) return false;
1251
+ const lastLine = contentLines[idx];
1252
+ return isIndentCode(lastLine);
1253
+ }
1254
+ function tryList(lines, i, end, opts) {
1255
+ const line = lines[i];
1256
+ const ulMatch = RE_UL_MARKER.exec(line);
1257
+ const olMatch = RE_OL_MARKER.exec(line);
1258
+ if (!ulMatch && !olMatch) return null;
1259
+ const ordered = !!olMatch;
1260
+ const startNum = olMatch ? parseInt(olMatch[1], 10) : void 0;
1261
+ const items = [];
1262
+ let j = i;
1263
+ let spread = false;
1264
+ while (j < end) {
1265
+ const currentLine = lines[j];
1266
+ const currentUl = RE_UL_MARKER.exec(currentLine);
1267
+ const currentOl = RE_OL_MARKER.exec(currentLine);
1268
+ let isListItem = ordered ? !!currentOl : !!currentUl;
1269
+ if (isListItem && j > i) {
1270
+ const currentStripped = strip3(currentLine);
1271
+ if (RE_THEMATIC_BREAK.test(currentStripped)) {
1272
+ isListItem = false;
1273
+ }
1274
+ }
1275
+ if (!isListItem && j > i) {
1276
+ const currentStripped = strip3(currentLine);
1277
+ if (RE_THEMATIC_BREAK.test(currentStripped)) {
1278
+ break;
1279
+ }
1280
+ if (isBlank(currentLine)) {
1281
+ let k = j + 1;
1282
+ while (k < end && isBlank(lines[k])) k++;
1283
+ if (k < end) {
1284
+ const nextUl = RE_UL_MARKER.exec(lines[k]);
1285
+ const nextOl = RE_OL_MARKER.exec(lines[k]);
1286
+ if (ordered ? !!nextOl : !!nextUl) {
1287
+ spread = true;
1288
+ j++;
1289
+ continue;
1290
+ }
1291
+ }
1292
+ break;
1293
+ }
1294
+ if (/^(?: {2,}|\t)/.test(currentLine)) {
1295
+ j++;
1296
+ continue;
1297
+ }
1298
+ break;
1299
+ }
1300
+ if (!isListItem) break;
1301
+ const marker = ordered ? currentOl : currentUl;
1302
+ const markerWidth = marker[0].length;
1303
+ let content = currentLine.slice(markerWidth);
1304
+ let checked;
1305
+ const taskMatch = RE_TASK.exec(content);
1306
+ if (taskMatch) {
1307
+ checked = taskMatch[1] !== " ";
1308
+ content = content.slice(taskMatch[0].length);
1309
+ }
1310
+ const itemLines = [content];
1311
+ j++;
1312
+ while (j < end) {
1313
+ const itemLine = lines[j];
1314
+ if (isBlank(itemLine)) {
1315
+ if (j + 1 < end && /^(?: {2,}|\t)/.test(lines[j + 1])) {
1316
+ itemLines.push("");
1317
+ j++;
1318
+ continue;
1319
+ }
1320
+ break;
1321
+ }
1322
+ if ((ordered ? RE_OL_MARKER : RE_UL_MARKER).test(itemLine)) break;
1323
+ if (RE_THEMATIC_BREAK.test(strip3(itemLine))) break;
1324
+ if (/^(?: {2,}|\t)/.test(itemLine)) {
1325
+ itemLines.push(itemLine.replace(/^(?: {2,}|\t)/, ""));
1326
+ j++;
1327
+ continue;
1328
+ }
1329
+ itemLines.push(itemLine);
1330
+ j++;
1331
+ }
1332
+ const children = parseBlockLines(itemLines, 0, itemLines.length, opts);
1333
+ items.push(createListItem(children, false, checked));
1334
+ }
1335
+ if (items.length === 0) return null;
1336
+ return {
1337
+ node: createList(ordered, spread, items, startNum),
1338
+ nextLine: j
1339
+ };
1340
+ }
1341
+ function tryHtmlBlock(lines, i, end) {
1342
+ const line = lines[i];
1343
+ const stripped = strip3(line);
1344
+ if (RE_HTML_BLOCK_1.test(stripped)) {
1345
+ const htmlLines = [];
1346
+ let j = i;
1347
+ while (j < end) {
1348
+ htmlLines.push(lines[j]);
1349
+ if (/<\/(?:script|pre|style|textarea)>/i.test(lines[j])) {
1350
+ j++;
1351
+ break;
1352
+ }
1353
+ j++;
1354
+ }
1355
+ return {
1356
+ node: createHtmlBlock(htmlLines.join("\n")),
1357
+ nextLine: j
1358
+ };
1359
+ }
1360
+ if (RE_HTML_BLOCK_2.test(stripped)) {
1361
+ const htmlLines = [];
1362
+ let j = i;
1363
+ while (j < end) {
1364
+ htmlLines.push(lines[j]);
1365
+ if (lines[j].includes("-->")) {
1366
+ j++;
1367
+ break;
1368
+ }
1369
+ j++;
1370
+ }
1371
+ return {
1372
+ node: createHtmlBlock(htmlLines.join("\n")),
1373
+ nextLine: j
1374
+ };
1375
+ }
1376
+ if (RE_HTML_BLOCK_3.test(stripped)) {
1377
+ const htmlLines = [];
1378
+ let j = i;
1379
+ while (j < end) {
1380
+ htmlLines.push(lines[j]);
1381
+ if (lines[j].includes("?>")) {
1382
+ j++;
1383
+ break;
1384
+ }
1385
+ j++;
1386
+ }
1387
+ return { node: createHtmlBlock(htmlLines.join("\n")), nextLine: j };
1388
+ }
1389
+ if (RE_HTML_BLOCK_4.test(stripped)) {
1390
+ const htmlLines = [];
1391
+ let j = i;
1392
+ while (j < end) {
1393
+ htmlLines.push(lines[j]);
1394
+ if (lines[j].includes(">")) {
1395
+ j++;
1396
+ break;
1397
+ }
1398
+ j++;
1399
+ }
1400
+ return { node: createHtmlBlock(htmlLines.join("\n")), nextLine: j };
1401
+ }
1402
+ if (RE_HTML_BLOCK_5.test(stripped)) {
1403
+ const htmlLines = [];
1404
+ let j = i;
1405
+ while (j < end) {
1406
+ htmlLines.push(lines[j]);
1407
+ if (lines[j].includes("]]>")) {
1408
+ j++;
1409
+ break;
1410
+ }
1411
+ j++;
1412
+ }
1413
+ return { node: createHtmlBlock(htmlLines.join("\n")), nextLine: j };
1414
+ }
1415
+ if (RE_HTML_BLOCK_6.test(stripped)) {
1416
+ const htmlLines = [];
1417
+ let j = i;
1418
+ while (j < end) {
1419
+ if (j > i && isBlank(lines[j])) break;
1420
+ htmlLines.push(lines[j]);
1421
+ j++;
1422
+ }
1423
+ return {
1424
+ node: createHtmlBlock(htmlLines.join("\n")),
1425
+ nextLine: j
1426
+ };
1427
+ }
1428
+ if (RE_HTML_BLOCK_7_OPEN.test(stripped) || RE_HTML_BLOCK_7_CLOSE.test(stripped)) {
1429
+ const htmlLines = [];
1430
+ let j = i;
1431
+ while (j < end) {
1432
+ if (j > i && isBlank(lines[j])) break;
1433
+ htmlLines.push(lines[j]);
1434
+ j++;
1435
+ }
1436
+ return {
1437
+ node: createHtmlBlock(htmlLines.join("\n")),
1438
+ nextLine: j
1439
+ };
1440
+ }
1441
+ return null;
1442
+ }
1443
+ function tryTable(lines, i, end, _opts) {
1444
+ if (i + 1 >= end) return null;
1445
+ const headerLine = lines[i];
1446
+ const delimLine = lines[i + 1];
1447
+ if (!RE_TABLE_DELIM.test(delimLine)) return null;
1448
+ if (!headerLine.includes("|")) return null;
1449
+ const align = parseTableAlign(delimLine);
1450
+ const headerCells = parseTableRow(headerLine);
1451
+ const headerRow = createTableRow(
1452
+ true,
1453
+ headerCells.map((cellText) => {
1454
+ const cell = createTableCell(inlineOrLazy(cellText, _opts));
1455
+ if (_opts.lazyInline && cellText) cell._raw = cellText;
1456
+ return cell;
1457
+ })
1458
+ );
1459
+ const rows = [headerRow];
1460
+ let j = i + 2;
1461
+ while (j < end) {
1462
+ const rowLine = lines[j];
1463
+ if (isBlank(rowLine)) break;
1464
+ if (!rowLine.includes("|")) break;
1465
+ const cells = parseTableRow(rowLine);
1466
+ rows.push(
1467
+ createTableRow(
1468
+ false,
1469
+ cells.map((cellText) => {
1470
+ const cell = createTableCell(inlineOrLazy(cellText, _opts));
1471
+ if (_opts.lazyInline && cellText) cell._raw = cellText;
1472
+ return cell;
1473
+ })
1474
+ )
1475
+ );
1476
+ j++;
1477
+ }
1478
+ return {
1479
+ node: createTable(align, rows),
1480
+ nextLine: j
1481
+ };
1482
+ }
1483
+ function parseTableAlign(delim) {
1484
+ return delim.replace(/^\||\|$/g, "").split("|").map((cell) => {
1485
+ const trimmed = cell.trim();
1486
+ const left = trimmed.startsWith(":");
1487
+ const right = trimmed.endsWith(":");
1488
+ if (left && right) return "center";
1489
+ if (right) return "right";
1490
+ if (left) return "left";
1491
+ return null;
1492
+ });
1493
+ }
1494
+ function parseTableRow(line) {
1495
+ let trimmed = line.trim();
1496
+ if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
1497
+ if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
1498
+ return trimmed.split("|").map((cell) => cell.trim());
1499
+ }
1500
+ function tryIndentedCode(lines, i, end) {
1501
+ const line = lines[i];
1502
+ if (!isIndentCode(line)) return null;
1503
+ const codeLines = [];
1504
+ let j = i;
1505
+ while (j < end) {
1506
+ const current = lines[j];
1507
+ if (isIndentCode(current)) {
1508
+ codeLines.push(current.replace(/^(?: {4}|\t)/, ""));
1509
+ j++;
1510
+ } else if (isBlank(current)) {
1511
+ codeLines.push("");
1512
+ j++;
1513
+ } else {
1514
+ break;
1515
+ }
1516
+ }
1517
+ while (codeLines.length > 0 && codeLines[codeLines.length - 1] === "") {
1518
+ codeLines.pop();
1519
+ }
1520
+ if (codeLines.length === 0) return null;
1521
+ return {
1522
+ node: createCodeBlock(codeLines.join("\n")),
1523
+ nextLine: j
1524
+ };
1525
+ }
1526
+ function tryParagraphOrSetext(lines, i, end, _opts) {
1527
+ const paragraphLines = [];
1528
+ let j = i;
1529
+ while (j < end) {
1530
+ const current = lines[j];
1531
+ if (isBlank(current)) break;
1532
+ if (paragraphLines.length > 0) {
1533
+ if (RE_SETEXT_H1.test(current)) {
1534
+ const content2 = paragraphLines.join("\n").trim();
1535
+ const node2 = createHeading(1, inlineOrLazy(content2, _opts));
1536
+ if (_opts.lazyInline && content2) node2._raw = content2;
1537
+ return {
1538
+ node: node2,
1539
+ nextLine: j + 1
1540
+ };
1541
+ }
1542
+ if (RE_SETEXT_H2.test(current)) {
1543
+ const content2 = paragraphLines.join("\n").trim();
1544
+ const node2 = createHeading(2, inlineOrLazy(content2, _opts));
1545
+ if (_opts.lazyInline && content2) node2._raw = content2;
1546
+ return {
1547
+ node: node2,
1548
+ nextLine: j + 1
1549
+ };
1550
+ }
1551
+ }
1552
+ if (paragraphLines.length > 0) {
1553
+ const currentStripped = strip3(current);
1554
+ if (RE_ATX_HEADING.test(currentStripped) || RE_THEMATIC_BREAK.test(currentStripped) || RE_FENCE_OPEN.test(currentStripped) || RE_BLOCKQUOTE.test(currentStripped) || RE_HTML_BLOCK_1.test(currentStripped) || RE_HTML_BLOCK_2.test(currentStripped) || RE_HTML_BLOCK_3.test(currentStripped) || RE_HTML_BLOCK_4.test(currentStripped) || RE_HTML_BLOCK_5.test(currentStripped) || RE_HTML_BLOCK_6.test(currentStripped)) {
1555
+ break;
1556
+ }
1557
+ }
1558
+ paragraphLines.push(current.replace(/^ {1,3}(?! )/, ""));
1559
+ j++;
1560
+ }
1561
+ if (paragraphLines.length === 0) return null;
1562
+ const content = paragraphLines.join("\n").trim();
1563
+ const node = createParagraph(inlineOrLazy(content, _opts));
1564
+ if (_opts.lazyInline && content) node._raw = content;
1565
+ return {
1566
+ node,
1567
+ nextLine: j
1568
+ };
1569
+ }
1570
+ function tryFrontMatter(lines, i, end) {
1571
+ const line = lines[i];
1572
+ if (!RE_FRONTMATTER_OPEN.test(line)) return null;
1573
+ const contentLines = [];
1574
+ let j = i + 1;
1575
+ let foundClose = false;
1576
+ while (j < end) {
1577
+ if (RE_FRONTMATTER_OPEN.test(lines[j])) {
1578
+ foundClose = true;
1579
+ j++;
1580
+ break;
1581
+ }
1582
+ contentLines.push(lines[j]);
1583
+ j++;
1584
+ }
1585
+ if (!foundClose) return null;
1586
+ const hasContent = contentLines.some((l) => l.trim().length > 0);
1587
+ if (!hasContent) return null;
1588
+ const looksLikeYaml = contentLines.some((l) => /[:{\[]/.test(l));
1589
+ if (!looksLikeYaml) return null;
1590
+ const content = contentLines.join("\n");
1591
+ return {
1592
+ node: createHtmlBlock(`<!-- frontmatter
1593
+ ${content}
1594
+ -->`),
1595
+ nextLine: j
1596
+ };
1597
+ }
1598
+ function tryDetail(lines, i, end, opts) {
1599
+ const line = lines[i];
1600
+ const openMatch = RE_DETAIL_OPEN.exec(line);
1601
+ if (!openMatch) return null;
1602
+ const isOpen = openMatch[1] === "-";
1603
+ const summary = openMatch[2].trim();
1604
+ const contentLines = [];
1605
+ let j = i + 1;
1606
+ while (j < end) {
1607
+ if (RE_DETAIL_CLOSE.test(lines[j])) {
1608
+ j++;
1609
+ break;
1610
+ }
1611
+ contentLines.push(lines[j]);
1612
+ j++;
1613
+ }
1614
+ const children = parseBlockLines(contentLines, 0, contentLines.length, opts);
1615
+ const detailSummary = isOpen ? summary : summary;
1616
+ return {
1617
+ node: createDetails(detailSummary, children),
1618
+ nextLine: j
1619
+ };
1620
+ }
1621
+ function tryFootnoteDefinition(lines, i, end, opts) {
1622
+ const line = lines[i];
1623
+ const match = RE_FOOTNOTE_DEF.exec(line);
1624
+ if (!match) return null;
1625
+ const identifier = match[1];
1626
+ const firstLineContent = match[2];
1627
+ const contentLines = [firstLineContent];
1628
+ let j = i + 1;
1629
+ while (j < end) {
1630
+ const current = lines[j];
1631
+ if (isBlank(current)) {
1632
+ if (j + 1 < end && /^(?: {2,}|\t)/.test(lines[j + 1])) {
1633
+ contentLines.push("");
1634
+ j++;
1635
+ continue;
1636
+ }
1637
+ break;
1638
+ }
1639
+ if (/^(?: {2,}|\t)/.test(current)) {
1640
+ contentLines.push(current.replace(/^(?: {2,}|\t)/, ""));
1641
+ j++;
1642
+ continue;
1643
+ }
1644
+ break;
1645
+ }
1646
+ const children = parseBlockLines(contentLines, 0, contentLines.length, opts);
1647
+ return {
1648
+ node: createFootnoteDefinition(identifier, identifier, children),
1649
+ nextLine: j
1650
+ };
1651
+ }
1652
+ function decodeInfoString(str) {
1653
+ let result = str.replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g, "$1");
1654
+ if (result.includes("&")) {
1655
+ result = result.replace(/&(?:#x([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g, (match, hex, dec, name) => {
1656
+ if (hex) return String.fromCodePoint(parseInt(hex, 16) || 65533);
1657
+ if (dec) return String.fromCodePoint(parseInt(dec, 10) || 65533);
1658
+ const entities = { amp: "&", lt: "<", gt: ">", quot: '"', ouml: "\xF6", uuml: "\xFC", auml: "\xE4" };
1659
+ return entities[name] ?? match;
1660
+ });
1661
+ }
1662
+ return result;
1663
+ }
1664
+
1665
+ // src/incremental.ts
1666
+ import { createDocument as createDocument2 } from "@pre-markdown/core";
1667
+ function fnv1a(str) {
1668
+ let hash = 2166136261 | 0;
1669
+ for (let i = 0; i < str.length; i++) {
1670
+ hash ^= str.charCodeAt(i);
1671
+ hash = hash * 16777619 | 0;
1672
+ }
1673
+ return hash >>> 0;
1674
+ }
1675
+ function combineHashes(hashes, from, to) {
1676
+ let h = 2166136261 | 0;
1677
+ for (let i = from; i < to; i++) {
1678
+ h ^= hashes[i];
1679
+ h = h * 16777619 | 0;
1680
+ }
1681
+ return h >>> 0;
1682
+ }
1683
+ var LRUBlockCache = class {
1684
+ map = /* @__PURE__ */ new Map();
1685
+ capacity;
1686
+ constructor(capacity = 256) {
1687
+ this.capacity = capacity;
1688
+ }
1689
+ get(fingerprint) {
1690
+ const node = this.map.get(fingerprint);
1691
+ if (node !== void 0) {
1692
+ this.map.delete(fingerprint);
1693
+ this.map.set(fingerprint, node);
1694
+ }
1695
+ return node;
1696
+ }
1697
+ set(fingerprint, node) {
1698
+ if (this.map.has(fingerprint)) {
1699
+ this.map.delete(fingerprint);
1700
+ }
1701
+ this.map.set(fingerprint, node);
1702
+ if (this.map.size > this.capacity) {
1703
+ const oldest = this.map.keys().next().value;
1704
+ this.map.delete(oldest);
1705
+ }
1706
+ }
1707
+ /** Bulk-populate cache from an array of fingerprint→node pairs */
1708
+ populate(metas, nodes) {
1709
+ for (let i = 0; i < metas.length; i++) {
1710
+ this.set(metas[i].fingerprint, nodes[i]);
1711
+ }
1712
+ }
1713
+ clear() {
1714
+ this.map.clear();
1715
+ }
1716
+ get size() {
1717
+ return this.map.size;
1718
+ }
1719
+ };
1720
+ var IncrementalParser = class {
1721
+ lines = [];
1722
+ lineHashes = [];
1723
+ document;
1724
+ options;
1725
+ eventBus = null;
1726
+ /** Per-block metadata tracking source line ranges and fingerprints */
1727
+ blockMetas = [];
1728
+ /** LRU cache: block fingerprint → previously parsed BlockNode subtree */
1729
+ blockCache = new LRUBlockCache(256);
1730
+ constructor(initialText = "", options, eventBus) {
1731
+ this.options = {
1732
+ gfmTables: true,
1733
+ mathBlocks: true,
1734
+ containers: true,
1735
+ toc: true,
1736
+ footnotes: true,
1737
+ lazyInline: false,
1738
+ ...options
1739
+ };
1740
+ this.eventBus = eventBus ?? null;
1741
+ this.lines = initialText.split("\n");
1742
+ this.lineHashes = this.lines.map(fnv1a);
1743
+ this.document = parseBlocks(initialText, this.options);
1744
+ this.buildBlockMetas();
1745
+ }
1746
+ /** Get current document AST */
1747
+ getDocument() {
1748
+ return this.document;
1749
+ }
1750
+ /** Get current source text */
1751
+ getText() {
1752
+ return this.lines.join("\n");
1753
+ }
1754
+ /** Get current lines */
1755
+ getLines() {
1756
+ return this.lines;
1757
+ }
1758
+ /** Get current line hashes (FNV-1a) */
1759
+ getLineHashes() {
1760
+ return this.lineHashes;
1761
+ }
1762
+ /** Get block metadata (for testing/debugging) */
1763
+ getBlockMetas() {
1764
+ return this.blockMetas;
1765
+ }
1766
+ /**
1767
+ * Apply an edit and incrementally reparse.
1768
+ */
1769
+ applyEdit(edit) {
1770
+ const startTime = performance.now();
1771
+ const newLines = edit.newText.split("\n");
1772
+ const newHashes = newLines.map(fnv1a);
1773
+ const deletedLineCount = edit.toLine - edit.fromLine;
1774
+ const insertedLineCount = newLines.length;
1775
+ const lineDelta = insertedLineCount - deletedLineCount;
1776
+ this.lines.splice(edit.fromLine, deletedLineCount, ...newLines);
1777
+ this.lineHashes.splice(edit.fromLine, deletedLineCount, ...newHashes);
1778
+ const { blockStart, blockEnd } = this.findAffectedBlockRangeFromMetas(
1779
+ edit.fromLine,
1780
+ edit.toLine,
1781
+ lineDelta
1782
+ );
1783
+ const reparseFrom = blockStart < this.blockMetas.length ? this.blockMetas[blockStart].startLine : this.lines.length;
1784
+ let reparseTo;
1785
+ if (blockEnd < this.blockMetas.length) {
1786
+ reparseTo = this.blockMetas[blockEnd].startLine + lineDelta;
1787
+ } else {
1788
+ reparseTo = this.lines.length;
1789
+ }
1790
+ reparseTo = Math.min(reparseTo, this.lines.length);
1791
+ const reparseFromClamped = Math.max(0, Math.min(reparseFrom, this.lines.length));
1792
+ const linesToReparse = this.lines.slice(reparseFromClamped, reparseTo);
1793
+ const newBlocks = linesToReparse.length > 0 ? parseBlockLines(linesToReparse, 0, linesToReparse.length, this.options) : [];
1794
+ const oldBlockCount = blockEnd - blockStart;
1795
+ const oldChildren = this.document.children;
1796
+ let cacheHits = 0;
1797
+ if (newBlocks.length > 0) {
1798
+ let newLineAcc = reparseFromClamped;
1799
+ for (let bi = 0; bi < newBlocks.length; bi++) {
1800
+ const block = newBlocks[bi];
1801
+ const blockLineCount = this.estimateBlockLineCount(block);
1802
+ const blockEndLine = Math.min(newLineAcc + blockLineCount, this.lines.length);
1803
+ const fp = combineHashes(this.lineHashes, newLineAcc, blockEndLine);
1804
+ const cached = this.blockCache.get(fp);
1805
+ if (cached && cached.type === block.type) {
1806
+ newBlocks[bi] = cached;
1807
+ cacheHits++;
1808
+ }
1809
+ newLineAcc = blockEndLine;
1810
+ }
1811
+ }
1812
+ const beforeBlocks = oldChildren.slice(0, blockStart);
1813
+ const afterBlocks = oldChildren.slice(blockEnd);
1814
+ const updatedChildren = [
1815
+ ...beforeBlocks,
1816
+ ...newBlocks,
1817
+ ...afterBlocks
1818
+ ];
1819
+ const reusedBlockCount = beforeBlocks.length + afterBlocks.length + cacheHits;
1820
+ this.document = createDocument2(updatedChildren);
1821
+ this.buildBlockMetas();
1822
+ const duration = performance.now() - startTime;
1823
+ if (this.eventBus) {
1824
+ this.eventBus.emit("content:change", {
1825
+ text: this.getText(),
1826
+ from: edit.fromLine,
1827
+ to: edit.toLine,
1828
+ inserted: edit.newText
1829
+ });
1830
+ this.eventBus.emit("parse:done", {
1831
+ documentId: this.document.id ?? 0,
1832
+ duration
1833
+ });
1834
+ }
1835
+ return {
1836
+ document: this.document,
1837
+ affectedRange: { from: reparseFromClamped, to: reparseTo },
1838
+ newBlockCount: newBlocks.length,
1839
+ oldBlockCount,
1840
+ reusedBlockCount,
1841
+ duration
1842
+ };
1843
+ }
1844
+ /**
1845
+ * Full reparse — used when edits are too complex for incremental update.
1846
+ */
1847
+ fullReparse() {
1848
+ const startTime = performance.now();
1849
+ const text = this.lines.join("\n");
1850
+ this.lineHashes = this.lines.map(fnv1a);
1851
+ this.document = parseBlocks(text, this.options);
1852
+ this.buildBlockMetas();
1853
+ const duration = performance.now() - startTime;
1854
+ if (this.eventBus) {
1855
+ this.eventBus.emit("parse:done", {
1856
+ documentId: this.document.id ?? 0,
1857
+ duration
1858
+ });
1859
+ }
1860
+ return this.document;
1861
+ }
1862
+ /**
1863
+ * Build block metadata array from current document + lines.
1864
+ * Assigns each block its source line range and fingerprint.
1865
+ */
1866
+ buildBlockMetas() {
1867
+ const children = this.document.children;
1868
+ const metas = new Array(children.length);
1869
+ let lineAcc = 0;
1870
+ for (let i = 0; i < children.length; i++) {
1871
+ const lc = this.estimateBlockLineCount(children[i]);
1872
+ const startLine = lineAcc;
1873
+ const endLine = Math.min(lineAcc + lc, this.lines.length);
1874
+ const fingerprint = combineHashes(this.lineHashes, startLine, endLine);
1875
+ metas[i] = { startLine, endLine, fingerprint };
1876
+ this.blockCache.set(fingerprint, children[i]);
1877
+ lineAcc = endLine;
1878
+ }
1879
+ this.blockMetas = metas;
1880
+ }
1881
+ /**
1882
+ * Find affected block range using block metas for precise mapping.
1883
+ * Returns [blockStart, blockEnd) — the half-open range of blocks to replace.
1884
+ */
1885
+ findAffectedBlockRangeFromMetas(editFromLine, editToLine, _lineDelta) {
1886
+ const metas = this.blockMetas;
1887
+ if (metas.length === 0) {
1888
+ return { blockStart: 0, blockEnd: 0 };
1889
+ }
1890
+ let blockStart = 0;
1891
+ {
1892
+ let lo = 0, hi = metas.length - 1;
1893
+ while (lo <= hi) {
1894
+ const mid = lo + hi >>> 1;
1895
+ if (metas[mid].endLine <= editFromLine) {
1896
+ lo = mid + 1;
1897
+ } else {
1898
+ blockStart = mid;
1899
+ hi = mid - 1;
1900
+ }
1901
+ }
1902
+ }
1903
+ blockStart = Math.max(0, blockStart - 1);
1904
+ let blockEnd = metas.length;
1905
+ {
1906
+ let lo = blockStart, hi = metas.length - 1;
1907
+ while (lo <= hi) {
1908
+ const mid = lo + hi >>> 1;
1909
+ if (metas[mid].startLine < editToLine) {
1910
+ lo = mid + 1;
1911
+ } else {
1912
+ blockEnd = mid;
1913
+ hi = mid - 1;
1914
+ }
1915
+ }
1916
+ }
1917
+ blockEnd = Math.min(metas.length, blockEnd + 1);
1918
+ return { blockStart, blockEnd };
1919
+ }
1920
+ /**
1921
+ * Estimate how many source lines a block node spans.
1922
+ * This is a heuristic — precise tracking would require SourceLocation data.
1923
+ */
1924
+ estimateBlockLineCount(node) {
1925
+ switch (node.type) {
1926
+ case "heading":
1927
+ return 2;
1928
+ // heading + blank line
1929
+ case "paragraph": {
1930
+ if (node._raw) {
1931
+ const nlCount = node._raw.split("\n").length;
1932
+ return nlCount + 1;
1933
+ }
1934
+ let textLen = 0;
1935
+ for (const child of node.children) {
1936
+ if (child.type === "text") textLen += child.value.length;
1937
+ if (child.type === "softBreak") textLen += 1;
1938
+ }
1939
+ return Math.max(2, Math.ceil(textLen / 80) + 1);
1940
+ }
1941
+ case "codeBlock": {
1942
+ const lineCount = node.value.split("\n").length;
1943
+ return lineCount + 3;
1944
+ }
1945
+ case "blockquote": {
1946
+ let count = 0;
1947
+ for (const child of node.children) {
1948
+ count += this.estimateBlockLineCount(child);
1949
+ }
1950
+ return Math.max(2, count + 1);
1951
+ }
1952
+ case "list": {
1953
+ let count = 0;
1954
+ for (const item of node.children) {
1955
+ count += this.estimateBlockLineCount(item);
1956
+ }
1957
+ return count + 1;
1958
+ }
1959
+ case "listItem": {
1960
+ let count = 1;
1961
+ for (const child of node.children) {
1962
+ count += this.estimateBlockLineCount(child) - 1;
1963
+ }
1964
+ return Math.max(1, count);
1965
+ }
1966
+ case "table": {
1967
+ return node.children.length + 2;
1968
+ }
1969
+ case "thematicBreak":
1970
+ return 2;
1971
+ case "mathBlock": {
1972
+ const lines = node.value.split("\n").length;
1973
+ return lines + 3;
1974
+ }
1975
+ case "container": {
1976
+ let count = 2;
1977
+ for (const child of node.children) {
1978
+ count += this.estimateBlockLineCount(child);
1979
+ }
1980
+ return count + 1;
1981
+ }
1982
+ case "details": {
1983
+ let count = 2;
1984
+ for (const child of node.children) {
1985
+ count += this.estimateBlockLineCount(child);
1986
+ }
1987
+ return count + 1;
1988
+ }
1989
+ case "footnoteDefinition": {
1990
+ let count = 1;
1991
+ for (const child of node.children) {
1992
+ count += this.estimateBlockLineCount(child);
1993
+ }
1994
+ return count + 1;
1995
+ }
1996
+ default:
1997
+ return 2;
1998
+ }
1999
+ }
2000
+ };
2001
+
2002
+ // src/index.ts
2003
+ function parse(input, options) {
2004
+ return parseBlocks(input, options);
2005
+ }
2006
+ export {
2007
+ IncrementalParser,
2008
+ parse,
2009
+ parseBlockLines,
2010
+ parseBlocks,
2011
+ parseInline
2012
+ };
2013
+ //# sourceMappingURL=index.js.map