@ai-matrx/content-ir 0.15.1 → 0.16.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/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: [] });
@@ -993,6 +1077,7 @@ var Tokenizer = class {
993
1077
  continue;
994
1078
  }
995
1079
  if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
1080
+ if (this.isOpenTail(k)) break;
996
1081
  last = k;
997
1082
  }
998
1083
  return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
@@ -1154,6 +1239,7 @@ var Tokenizer = class {
1154
1239
  const li = this.lineOf(tp);
1155
1240
  const ce = this.contentEnds[li];
1156
1241
  const tag = readXmlTag(this.text, tp, this.finder);
1242
+ this.tick(tag ? tag.raw.length : 1);
1157
1243
  let openEnd;
1158
1244
  if (tag && !tag.isClosing) {
1159
1245
  if (tag.isSelfClosing) {
@@ -1238,7 +1324,7 @@ var Tokenizer = class {
1238
1324
  return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
1239
1325
  }
1240
1326
  const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
1241
- const typeSeven = atParaStart && t.trimEnd() === tag.raw;
1327
+ const typeSeven = atParaStart && t.trimEnd() === tag.raw && !this.isOpenTail(li);
1242
1328
  if (!typeSix && !typeSeven) return null;
1243
1329
  let end = this.contentEnds[li];
1244
1330
  for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
@@ -1330,6 +1416,10 @@ var Tokenizer = class {
1330
1416
  let breakLine = -1;
1331
1417
  for (let k = li + 1; k < this.lineCount; k++) {
1332
1418
  if (this.lineIsBlank(k)) break;
1419
+ if (this.lineStarts[k] >= this.provisionalFrom) {
1420
+ end = this.n;
1421
+ break;
1422
+ }
1333
1423
  if (this.detectBlock(this.lineStarts[k], k, false)) {
1334
1424
  breakLine = k;
1335
1425
  break;
@@ -1395,6 +1485,7 @@ var Tokenizer = class {
1395
1485
  scanInline(ps, pe) {
1396
1486
  const text = this.text;
1397
1487
  const inlines = [];
1488
+ this.tick(pe - ps);
1398
1489
  const hasKind = text.slice(ps, pe).includes("__kind");
1399
1490
  let kindBudget = 4 * (pe - ps) + 65536;
1400
1491
  const stop = (island, orphan = false) => ({ inlines, breakIsland: island, orphan });
@@ -1405,6 +1496,7 @@ var Tokenizer = class {
1405
1496
  let runs = null;
1406
1497
  const codeSpanClose = (n, from) => {
1407
1498
  if (!runs) {
1499
+ this.tick(pe - ps);
1408
1500
  runs = /* @__PURE__ */ new Map();
1409
1501
  for (let k = ps; k < pe; ) {
1410
1502
  if (text.charCodeAt(k) !== 96) {
@@ -1431,7 +1523,8 @@ var Tokenizer = class {
1431
1523
  return at !== void 0 && at + n <= pe ? at : -1;
1432
1524
  };
1433
1525
  let i = ps;
1434
- while (i < pe) {
1526
+ const scanEnd = Math.min(pe, this.provisionalFrom);
1527
+ while (i < scanEnd) {
1435
1528
  const ch = text[i];
1436
1529
  if (ch === "\\") {
1437
1530
  const nextCh = text[i + 1];
@@ -1449,6 +1542,9 @@ var Tokenizer = class {
1449
1542
  if (ch === "`") {
1450
1543
  const n = backtickRunLength(text, i);
1451
1544
  const close = codeSpanClose(n, i + n);
1545
+ if (close === -1 && this.streaming && this.paragraphMayGrow(pe)) {
1546
+ return { inlines, breakIsland: null, orphan: false };
1547
+ }
1452
1548
  i = close === -1 ? i + n : close + n;
1453
1549
  continue;
1454
1550
  }
@@ -1465,6 +1561,7 @@ var Tokenizer = class {
1465
1561
  continue;
1466
1562
  }
1467
1563
  const end = singleDollarMathEnd(text, i, pe);
1564
+ this.tick(end > 0 ? end - i : Math.min(403, pe - i));
1468
1565
  if (end > 0 && !(this.streaming && end === this.n)) {
1469
1566
  inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
1470
1567
  i = end;
@@ -1492,6 +1589,7 @@ var Tokenizer = class {
1492
1589
  const limit = Math.min(pe, i + 65536);
1493
1590
  const end = matchingJsonObjectEnd(text, i, limit);
1494
1591
  kindBudget -= (end ?? limit) - i;
1592
+ this.tick((end ?? limit) - i);
1495
1593
  if (end !== null) {
1496
1594
  const kind = declaredKind(text.slice(i, end));
1497
1595
  if (kind) {
@@ -1553,6 +1651,7 @@ var Tokenizer = class {
1553
1651
  continue;
1554
1652
  }
1555
1653
  const tag = readXmlTag(text, i, this.finder);
1654
+ this.tick(tag ? tag.raw.length : 1);
1556
1655
  if (tag && i + tag.raw.length <= pe) {
1557
1656
  const name = tag.tagName;
1558
1657
  const tagEnd = i + tag.raw.length;
@@ -1601,7 +1700,7 @@ var Tokenizer = class {
1601
1700
  }
1602
1701
  };
1603
1702
  function tokenizeSource(text, options = {}) {
1604
- const raw = new Tokenizer(text, options.streaming ?? false).run();
1703
+ const raw = new Tokenizer(text, options.streaming ?? false, options.meter).run();
1605
1704
  const cp = buildCodePointIndex(text);
1606
1705
  const inline = (island) => ({
1607
1706
  islandType: island.type,
@@ -1975,6 +2074,7 @@ exports.mapPosition = mapPosition;
1975
2074
  exports.mapRange = mapRange;
1976
2075
  exports.pairDisplayMath = pairDisplayMath;
1977
2076
  exports.parseFenceOpener = parseFenceOpener;
2077
+ exports.pendingDisplayMathOpener = pendingDisplayMathOpener;
1978
2078
  exports.readXmlTag = readXmlTag;
1979
2079
  exports.singleDollarMathEnd = singleDollarMathEnd;
1980
2080
  exports.spliceSave = spliceSave;