@ai-matrx/content-ir 0.15.1 → 0.16.1

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/source.cjs CHANGED
@@ -348,6 +348,50 @@ function codeRanges(text) {
348
348
  }
349
349
  return ranges.sort((a, b) => a[0] - b[0]);
350
350
  }
351
+ function pendingDisplayMathOpener(text) {
352
+ if (!text.includes("$$")) return -1;
353
+ const ranges = codeRanges(text);
354
+ const tokens = [];
355
+ let r = 0;
356
+ let maxEnd = -1;
357
+ for (let i = text.indexOf("$$"); i !== -1 && i < text.length - 1; i = text.indexOf("$$", i)) {
358
+ while (r < ranges.length && ranges[r][0] <= i) {
359
+ maxEnd = Math.max(maxEnd, ranges[r][1]);
360
+ r++;
361
+ }
362
+ if (maxEnd <= i) tokens.push(i);
363
+ i += 2;
364
+ }
365
+ const paired = /* @__PURE__ */ new Set();
366
+ let j = 0;
367
+ while (j < tokens.length) {
368
+ const open = tokens[j];
369
+ const close = tokens[j + 1];
370
+ if (close === void 0) break;
371
+ if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
372
+ paired.add(open);
373
+ paired.add(close);
374
+ j += 2;
375
+ continue;
376
+ }
377
+ j += 1;
378
+ }
379
+ let trail = 0;
380
+ while (trail < text.length && /\s/.test(text[text.length - 1 - trail])) trail++;
381
+ for (const token of tokens) {
382
+ if (paired.has(token)) continue;
383
+ let lead = 0;
384
+ while (lead <= MAX_MATH_SPAN && /\s/.test(text[token + 2 + lead] ?? "x")) lead++;
385
+ if (text.length - (token + 2) - lead - trail > MAX_MATH_SPAN) continue;
386
+ const inner = text.slice(token + 2);
387
+ const s = inner.trim();
388
+ if (STRUCTURAL_MARKDOWN.test(s) || s.length > MAX_MATH_SPAN) continue;
389
+ if (!opensFlowBlock(text, token) && /\n[ \t]*\n/.test(inner)) continue;
390
+ if ((s.match(PROSE_WORD) ?? []).length >= PROSE_WORD_LIMIT * 3) continue;
391
+ return token;
392
+ }
393
+ return -1;
394
+ }
351
395
  function pairDisplayMath(text) {
352
396
  const pairs = /* @__PURE__ */ new Map();
353
397
  if (!text.includes("$$")) return pairs;
@@ -696,11 +740,15 @@ function isSplitterTreeLine(line) {
696
740
  }
697
741
  var MEDIA_REF_PREFIXES = ["[Image URL:", "[Video URL:", "[Audio URL:"];
698
742
  var Tokenizer = class {
699
- constructor(text, streaming = false) {
743
+ constructor(text, streaming = false, meter = void 0) {
700
744
  this.text = text;
701
745
  this.streaming = streaming;
746
+ this.meter = meter;
702
747
  this.n = text.length;
748
+ this.tick(text.length);
703
749
  this.mathPairs = pairDisplayMath(text);
750
+ this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
751
+ if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
704
752
  let start = 0;
705
753
  for (let i = 0; i < text.length; i++) {
706
754
  if (text.charCodeAt(i) === 10) {
@@ -711,13 +759,15 @@ var Tokenizer = class {
711
759
  }
712
760
  }
713
761
  if (start < text.length || text.length === 0) {
762
+ this.pendingCR = streaming && text.length > start && text.charCodeAt(text.length - 1) === 13;
714
763
  this.lineStarts.push(start);
715
- this.contentEnds.push(text.length);
764
+ this.contentEnds.push(this.pendingCR ? text.length - 1 : text.length);
716
765
  this.lineEnds.push(text.length);
717
766
  }
718
767
  }
719
768
  text;
720
769
  streaming;
770
+ meter;
721
771
  n;
722
772
  lineStarts = [];
723
773
  contentEnds = [];
@@ -732,6 +782,23 @@ var Tokenizer = class {
732
782
  /** `$$` opener → closer for the pairs the core renders as math. */
733
783
  mathPairs;
734
784
  finder = (needle, from) => this.next(needle, from);
785
+ /** Streaming: where provisional text starts (a pending `$$`), or +∞. */
786
+ provisionalFrom = Number.POSITIVE_INFINITY;
787
+ /** Streaming: the text ends in a lone CR whose LF has not arrived yet. */
788
+ pendingCR = false;
789
+ /**
790
+ * Streaming: an open last line that begins with `{` is undecided — each new
791
+ * byte can balance or unbalance its braces and flip the whole line between
792
+ * prose and a JSON island — so it is provisional until it ends (G5).
793
+ */
794
+ markOpenJsonTail() {
795
+ const last = this.lineStarts.length - 1;
796
+ if (!this.isOpenTail(last)) return;
797
+ const start = this.lineStarts[last];
798
+ if (this.lineText(last).trimStart().startsWith("{")) {
799
+ this.provisionalFrom = Math.min(this.provisionalFrom, start);
800
+ }
801
+ }
735
802
  /**
736
803
  * `text.indexOf(needle, from)` in O(log k): the first query for a needle
737
804
  * indexes all of its occurrences once, so repeated searches for a closer
@@ -755,9 +822,21 @@ var Tokenizer = class {
755
822
  }
756
823
  return lo < list.length ? list[lo] : -1;
757
824
  }
825
+ /** Charge `n` visited characters to the diagnostic meter (complexity tests). */
826
+ tick(n) {
827
+ if (this.meter) this.meter.work += n;
828
+ }
829
+ /** Streaming only: a paragraph ending at `pe` may still be continued by a line not yet arrived. */
830
+ paragraphMayGrow(pe) {
831
+ if (!this.streaming) return false;
832
+ const rest = this.n - pe;
833
+ if (rest === 0) return true;
834
+ const tail = this.text.slice(pe);
835
+ return tail === "\n" || tail === "\r\n" || tail === "\r";
836
+ }
758
837
  /** Streaming only: the final line, still waiting for its terminator. */
759
838
  isOpenTail(k) {
760
- return this.streaming && k === this.lineStarts.length - 1 && this.lineEnds[k] === this.contentEnds[k];
839
+ return this.streaming && k === this.lineStarts.length - 1 && (this.lineEnds[k] === this.contentEnds[k] || this.pendingCR);
761
840
  }
762
841
  get lineCount() {
763
842
  return this.lineStarts.length;
@@ -782,6 +861,7 @@ var Tokenizer = class {
782
861
  return this.text.slice(this.lineStarts[li], this.contentEnds[li]);
783
862
  }
784
863
  run() {
864
+ if (this.streaming) this.markOpenJsonTail();
785
865
  let pos = 0;
786
866
  while (pos < this.n) {
787
867
  const li = this.lineOf(pos);
@@ -790,6 +870,10 @@ var Tokenizer = class {
790
870
  pos = this.gap(pos, li);
791
871
  continue;
792
872
  }
873
+ if (pos >= this.provisionalFrom) {
874
+ this.push({ kind: "prose", start: pos, end: this.n, island: null, inlines: [] });
875
+ break;
876
+ }
793
877
  const island = this.detectBlock(pos, li, true);
794
878
  if (island) {
795
879
  this.push({ kind: "island", start: island.start, end: island.end, island, inlines: [] });
@@ -840,7 +924,8 @@ var Tokenizer = class {
840
924
  const fm = this.frontMatter(li, t);
841
925
  if (fm) return fm;
842
926
  }
843
- if (indent <= 3 && t.startsWith("::")) {
927
+ const listNested = indent > 0 && this.inListContinuation(li);
928
+ if (indent <= 3 && !listNested && t.startsWith("::")) {
844
929
  const directive = this.directive(p, li, t);
845
930
  if (directive) return directive;
846
931
  }
@@ -848,7 +933,7 @@ var Tokenizer = class {
848
933
  const admonition = this.mkdocsAdmonition(p, li, t);
849
934
  if (admonition) return admonition;
850
935
  }
851
- if (indent <= 3 && t.startsWith(">")) {
936
+ if (indent <= 3 && !listNested && t.startsWith(">")) {
852
937
  const callout = this.callout(p, li, t);
853
938
  if (callout) return callout;
854
939
  }
@@ -889,6 +974,17 @@ var Tokenizer = class {
889
974
  }
890
975
  return null;
891
976
  }
977
+ /** True when line `li` continues a list item (the nearest non-blank line above is a list item or indented text under one). */
978
+ inListContinuation(li) {
979
+ for (let k = li - 1; k >= 0; k--) {
980
+ const line = this.lineText(k);
981
+ if (!NOT_WHITESPACE.test(line)) continue;
982
+ if (/^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]/.test(line)) return true;
983
+ if (/^[ \t]/.test(line)) continue;
984
+ return false;
985
+ }
986
+ return false;
987
+ }
892
988
  island(type, start, end, complete, meta = {}) {
893
989
  return { type, start, end, complete, meta };
894
990
  }
@@ -993,6 +1089,7 @@ var Tokenizer = class {
993
1089
  continue;
994
1090
  }
995
1091
  if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
1092
+ if (this.isOpenTail(k)) break;
996
1093
  last = k;
997
1094
  }
998
1095
  return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
@@ -1154,6 +1251,7 @@ var Tokenizer = class {
1154
1251
  const li = this.lineOf(tp);
1155
1252
  const ce = this.contentEnds[li];
1156
1253
  const tag = readXmlTag(this.text, tp, this.finder);
1254
+ this.tick(tag ? tag.raw.length : 1);
1157
1255
  let openEnd;
1158
1256
  if (tag && !tag.isClosing) {
1159
1257
  if (tag.isSelfClosing) {
@@ -1238,7 +1336,7 @@ var Tokenizer = class {
1238
1336
  return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
1239
1337
  }
1240
1338
  const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
1241
- const typeSeven = atParaStart && t.trimEnd() === tag.raw;
1339
+ const typeSeven = atParaStart && t.trimEnd() === tag.raw && !this.isOpenTail(li);
1242
1340
  if (!typeSix && !typeSeven) return null;
1243
1341
  let end = this.contentEnds[li];
1244
1342
  for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
@@ -1330,6 +1428,10 @@ var Tokenizer = class {
1330
1428
  let breakLine = -1;
1331
1429
  for (let k = li + 1; k < this.lineCount; k++) {
1332
1430
  if (this.lineIsBlank(k)) break;
1431
+ if (this.lineStarts[k] >= this.provisionalFrom) {
1432
+ end = this.n;
1433
+ break;
1434
+ }
1333
1435
  if (this.detectBlock(this.lineStarts[k], k, false)) {
1334
1436
  breakLine = k;
1335
1437
  break;
@@ -1395,6 +1497,7 @@ var Tokenizer = class {
1395
1497
  scanInline(ps, pe) {
1396
1498
  const text = this.text;
1397
1499
  const inlines = [];
1500
+ this.tick(pe - ps);
1398
1501
  const hasKind = text.slice(ps, pe).includes("__kind");
1399
1502
  let kindBudget = 4 * (pe - ps) + 65536;
1400
1503
  const stop = (island, orphan = false) => ({ inlines, breakIsland: island, orphan });
@@ -1405,6 +1508,7 @@ var Tokenizer = class {
1405
1508
  let runs = null;
1406
1509
  const codeSpanClose = (n, from) => {
1407
1510
  if (!runs) {
1511
+ this.tick(pe - ps);
1408
1512
  runs = /* @__PURE__ */ new Map();
1409
1513
  for (let k = ps; k < pe; ) {
1410
1514
  if (text.charCodeAt(k) !== 96) {
@@ -1431,7 +1535,8 @@ var Tokenizer = class {
1431
1535
  return at !== void 0 && at + n <= pe ? at : -1;
1432
1536
  };
1433
1537
  let i = ps;
1434
- while (i < pe) {
1538
+ const scanEnd = Math.min(pe, this.provisionalFrom);
1539
+ while (i < scanEnd) {
1435
1540
  const ch = text[i];
1436
1541
  if (ch === "\\") {
1437
1542
  const nextCh = text[i + 1];
@@ -1449,6 +1554,9 @@ var Tokenizer = class {
1449
1554
  if (ch === "`") {
1450
1555
  const n = backtickRunLength(text, i);
1451
1556
  const close = codeSpanClose(n, i + n);
1557
+ if (close === -1 && this.streaming && this.paragraphMayGrow(pe)) {
1558
+ return { inlines, breakIsland: null, orphan: false };
1559
+ }
1452
1560
  i = close === -1 ? i + n : close + n;
1453
1561
  continue;
1454
1562
  }
@@ -1465,6 +1573,7 @@ var Tokenizer = class {
1465
1573
  continue;
1466
1574
  }
1467
1575
  const end = singleDollarMathEnd(text, i, pe);
1576
+ this.tick(end > 0 ? end - i : Math.min(403, pe - i));
1468
1577
  if (end > 0 && !(this.streaming && end === this.n)) {
1469
1578
  inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
1470
1579
  i = end;
@@ -1492,6 +1601,7 @@ var Tokenizer = class {
1492
1601
  const limit = Math.min(pe, i + 65536);
1493
1602
  const end = matchingJsonObjectEnd(text, i, limit);
1494
1603
  kindBudget -= (end ?? limit) - i;
1604
+ this.tick((end ?? limit) - i);
1495
1605
  if (end !== null) {
1496
1606
  const kind = declaredKind(text.slice(i, end));
1497
1607
  if (kind) {
@@ -1553,6 +1663,7 @@ var Tokenizer = class {
1553
1663
  continue;
1554
1664
  }
1555
1665
  const tag = readXmlTag(text, i, this.finder);
1666
+ this.tick(tag ? tag.raw.length : 1);
1556
1667
  if (tag && i + tag.raw.length <= pe) {
1557
1668
  const name = tag.tagName;
1558
1669
  const tagEnd = i + tag.raw.length;
@@ -1601,7 +1712,7 @@ var Tokenizer = class {
1601
1712
  }
1602
1713
  };
1603
1714
  function tokenizeSource(text, options = {}) {
1604
- const raw = new Tokenizer(text, options.streaming ?? false).run();
1715
+ const raw = new Tokenizer(text, options.streaming ?? false, options.meter).run();
1605
1716
  const cp = buildCodePointIndex(text);
1606
1717
  const inline = (island) => ({
1607
1718
  islandType: island.type,
@@ -1975,6 +2086,7 @@ exports.mapPosition = mapPosition;
1975
2086
  exports.mapRange = mapRange;
1976
2087
  exports.pairDisplayMath = pairDisplayMath;
1977
2088
  exports.parseFenceOpener = parseFenceOpener;
2089
+ exports.pendingDisplayMathOpener = pendingDisplayMathOpener;
1978
2090
  exports.readXmlTag = readXmlTag;
1979
2091
  exports.singleDollarMathEnd = singleDollarMathEnd;
1980
2092
  exports.spliceSave = spliceSave;