@ai-matrx/content-ir 0.18.4 → 0.18.5

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
@@ -323,6 +323,7 @@ var FenceReader = class {
323
323
  function closeFence(src, open, opener) {
324
324
  const first = readFence(src, open, new FenceReader(opener));
325
325
  if (first.closed || !first.reader.sawNested || first.reader.nestedDepth <= 0) return { line: first.line, closed: first.closed };
326
+ if (src.streaming) return { line: first.line, closed: false };
326
327
  const strict = readFence(src, open, new FenceReader(opener, { nesting: false }));
327
328
  return { line: strict.line, closed: strict.closed };
328
329
  }
@@ -586,6 +587,8 @@ function fenceLineKinds(text) {
586
587
  function pendingCodeSpanStart(text) {
587
588
  const n = text.length;
588
589
  if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
590
+ const lastLine = text.slice(text.lastIndexOf("\n") + 1);
591
+ if (lastLine && /^(?: {0,3}>[ \t]?)*[ \t]*$/.test(lastLine)) return pendingCodeSpanStart(`${text}x`);
589
592
  const s = scan(text);
590
593
  const para = s.pendingPara;
591
594
  if (s.endsInFence || para === -1) return -1;
@@ -732,13 +735,26 @@ var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
732
735
  function isSingleDollarMath(content, after) {
733
736
  if (!content || content.length > 400) return false;
734
737
  if (/^\s/.test(content) || /\s$/.test(content)) return false;
735
- if (after !== void 0 && /[0-9]/.test(after)) return false;
738
+ if (after !== void 0 && /^[0-9]/.test(after)) return false;
739
+ if (isVariableChain(content, after)) return false;
736
740
  if (/(?:^|[^\\])%/.test(content)) return false;
737
741
  if (/^[)\]}"/]/.test(content) || /[(\[{":;,/]$/.test(content)) return false;
738
- if (/^[0-9]/.test(content) && after !== void 0 && /[A-Za-z]/.test(after)) return false;
742
+ if (/^[0-9]/.test(content) && after !== void 0 && /^[A-Za-z]/.test(after)) return false;
739
743
  if (isMathPlaceholder(content)) return false;
740
744
  return TEX_SIGNAL.test(content) || !readsAsProse(content);
741
745
  }
746
+ var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
747
+ var MATH_SUFFIX = /^(?:s|es|th|st|nd|rd)$/i;
748
+ function isVariableChain(content, after) {
749
+ if (/^\{[A-Za-z_][\w.]*\}$/.test(content)) return true;
750
+ if (!after) return false;
751
+ if (after[0] === "{") return IDENTIFIER.test(content);
752
+ const run = /^[A-Za-z0-9_]+/.exec(after)?.[0];
753
+ if (!run) return false;
754
+ if (after.length === 1) return false;
755
+ if (/^[A-Za-z_][A-Za-z0-9_]*[.@-]$/.test(content)) return true;
756
+ return IDENTIFIER.test(content) && !MATH_SUFFIX.test(run);
757
+ }
742
758
  var FUNCTION_NAMES = /* @__PURE__ */ new Set([
743
759
  "sin",
744
760
  "cos",
@@ -782,6 +798,12 @@ function isMathShape(content) {
782
798
  if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
783
799
  return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
784
800
  }
801
+ var AFTER_WINDOW = 32;
802
+ function singleDollarAfter(text, close) {
803
+ const rest = text.slice(close + 1, close + 1 + AFTER_WINDOW);
804
+ return rest.length < AFTER_WINDOW ? `${rest}
805
+ ` : rest;
806
+ }
785
807
  function singleDollarMathEnd(text, open, limit = text.length) {
786
808
  const bound = Math.min(limit, open + 403);
787
809
  let close = -1;
@@ -799,7 +821,7 @@ function singleDollarMathEnd(text, open, limit = text.length) {
799
821
  }
800
822
  if (close === -1 || text[close + 1] === "$") return -1;
801
823
  const content = text.slice(open + 1, close);
802
- return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
824
+ return isSingleDollarMath(content, singleDollarAfter(text, close)) ? close + 1 : -1;
803
825
  }
804
826
  var BRACKET_TEX_SIGNAL = /\\[A-Za-z]+|[\^{}=<>+]|_\{|(?:^|[^A-Za-z0-9])[A-Za-z]_[A-Za-z0-9](?![A-Za-z0-9])/;
805
827
  function isEscapedBracketMath(content, before) {
@@ -1170,18 +1192,14 @@ var Tokenizer = class {
1170
1192
  /**
1171
1193
  * Streaming: an open last line that begins with `{` is undecided — each new
1172
1194
  * byte can balance or unbalance its braces and flip the whole line between
1173
- * prose and a JSON island — and so is a line that is exactly one allowed
1174
- * HTML tag so far; both are provisional until they end (G5).
1195
+ * prose and a JSON island — so it is provisional until it ends (G5).
1196
+ * (A lone HTML tag on that line is held by {@link holdUndecidedTail}.)
1175
1197
  */
1176
1198
  markOpenJsonTail() {
1177
1199
  const last = this.lineStarts.length - 1;
1178
1200
  if (!this.isOpenTail(last)) return;
1179
- const start = this.lineStarts[last];
1180
- const t = this.lineText(last).trim();
1181
- const oneTag = t.startsWith("<") ? readXmlTag(t, 0) : null;
1182
- const loneTag = !!oneTag && oneTag.raw === t && ALLOWED_RAW_HTML_TAGS.has(oneTag.tagName.toLowerCase());
1183
- if (t.startsWith("{") || loneTag) {
1184
- this.provisionalFrom = Math.min(this.provisionalFrom, start);
1201
+ if (this.lineText(last).trim().startsWith("{")) {
1202
+ this.provisionalFrom = Math.min(this.provisionalFrom, this.lineStarts[last]);
1185
1203
  }
1186
1204
  }
1187
1205
  /**
@@ -1214,11 +1232,22 @@ var Tokenizer = class {
1214
1232
  /** Streaming only: a paragraph ending at `pe` may still be continued by a line not yet arrived. */
1215
1233
  paragraphMayGrow(pe) {
1216
1234
  if (!this.streaming) return false;
1235
+ if (this.undecidedBreaks.has(pe)) return true;
1217
1236
  const rest = this.n - pe;
1218
1237
  if (rest === 0) return true;
1219
1238
  const tail = this.text.slice(pe);
1220
- return tail === "\n" || tail === "\r\n" || tail === "\r";
1239
+ return /^(?:\r\n|\r|\n)[ \t]*$/.test(tail);
1240
+ }
1241
+ /**
1242
+ * Streaming: an open block that may still turn back into prose — a `$$`
1243
+ * pair whose closer is on the open last line (prose after it makes it
1244
+ * inline), or a JSON head whose braces have not balanced (it may never parse).
1245
+ */
1246
+ mayDissolve(block) {
1247
+ return this.streaming && !block.complete && (block.type === "math_block" || block.type === "json");
1221
1248
  }
1249
+ /** Streaming: paragraph ends where an undecided, dissolvable block cut them short. */
1250
+ undecidedBreaks = /* @__PURE__ */ new Set();
1222
1251
  /** Streaming only: the final line, still waiting for its terminator. */
1223
1252
  isOpenTail(k) {
1224
1253
  return this.streaming && k === this.lineStarts.length - 1 && (this.lineEnds[k] === this.contentEnds[k] || this.pendingCR);
@@ -1267,8 +1296,103 @@ var Tokenizer = class {
1267
1296
  }
1268
1297
  pos = this.paragraph(pos, li);
1269
1298
  }
1299
+ if (this.streaming) this.holdUndecidedTail();
1270
1300
  return this.blocks;
1271
1301
  }
1302
+ /**
1303
+ * Streaming: settle nothing the next bytes can still change (verify-RC-B3 F3).
1304
+ *
1305
+ * 1. A block island that ends on the open last line is undecided — the next
1306
+ * bytes on that line can re-read it or dissolve it (`<!--@a:k1-->` +
1307
+ * ` more` is an inline anchor, `<br>` + `x` is prose). It runs open to
1308
+ * the end of the text.
1309
+ * 2. A box-drawing tree still forming at the end of the text claims, as its
1310
+ * root label, the lines directly above its first connector (the
1311
+ * renderer's `findTreeBlockStart`), and the open last line itself may
1312
+ * become one of its connector lines — so inlines on those lines are held
1313
+ * back as prose until a blank line, a heading or a tree line decides.
1314
+ * This also holds everything a closer still able to arrive on the open
1315
+ * last line could change (`$x$` + `5` is currency, G4; `{{v}}` + `}`).
1316
+ */
1317
+ holdUndecidedTail() {
1318
+ const last = this.lineStarts.length - 1;
1319
+ if (last < 0) return;
1320
+ if (this.isOpenTail(last)) {
1321
+ const lineStart = this.lineStarts[last];
1322
+ for (const block2 of this.blocks) {
1323
+ if (block2.kind === "prose" && block2.end > lineStart) {
1324
+ block2.inlines = block2.inlines.filter((inline) => inline.end <= lineStart);
1325
+ }
1326
+ }
1327
+ let b = this.blocks.length - 1;
1328
+ while (b >= 0 && this.blocks[b].kind === "gap") b--;
1329
+ const block = this.blocks[b];
1330
+ if (block?.island?.complete && block.island.end > this.lineStarts[last]) {
1331
+ block.island.complete = false;
1332
+ block.island.end = this.n;
1333
+ block.end = this.n;
1334
+ this.blocks.length = b + 1;
1335
+ }
1336
+ }
1337
+ const from = this.treeRootHoldFrom();
1338
+ for (let b = this.blocks.length - 1; b >= 0; b--) {
1339
+ const block = this.blocks[b];
1340
+ if (block.end <= from) break;
1341
+ if (block.kind === "prose") block.inlines = block.inlines.filter((inline) => inline.end <= from);
1342
+ if (block.island) this.blocks[b] = { kind: "prose", start: block.start, end: block.end, island: null, inlines: [] };
1343
+ }
1344
+ }
1345
+ /**
1346
+ * Streaming: where a tree that may still form at the end of the text would
1347
+ * start claiming lines (`n` when none can). Only paragraph lines can be
1348
+ * claimed — a line that starts inside an island (a fence body, an XML
1349
+ * region…) is never a tree line nor a root label, so it stops the walk.
1350
+ */
1351
+ treeRootHoldFrom() {
1352
+ const claimable = (k2) => {
1353
+ for (; ; ) {
1354
+ const block = this.blockAt(this.lineStarts[k2]);
1355
+ if (!block || block.kind !== "island" || block.island?.fromParagraph === true) return true;
1356
+ const opened = this.lineOf(block.start);
1357
+ if (this.lineStarts[opened] === block.start || opened === k2) return false;
1358
+ k2 = opened;
1359
+ }
1360
+ };
1361
+ let k = this.lineCount - 1;
1362
+ let start = this.lineCount;
1363
+ if (k >= 0 && this.isOpenTail(k)) {
1364
+ if (!claimable(k)) return this.n;
1365
+ start = k;
1366
+ k--;
1367
+ }
1368
+ for (; k >= 0 && claimable(k); k--) {
1369
+ this.tick(1);
1370
+ if (this.lineIsBlank(k)) continue;
1371
+ if (!TREE_GLYPHS.test(this.lineText(k))) break;
1372
+ start = k;
1373
+ }
1374
+ let root = start;
1375
+ for (let j = start - 1; j >= 0 && claimable(j); j--) {
1376
+ const trimmed = this.lineText(j).trim();
1377
+ this.tick(trimmed.length + 1);
1378
+ if (!trimmed || isSplitterTreeLine(trimmed) || /^#{1,6}\s/.test(trimmed)) break;
1379
+ root = j;
1380
+ }
1381
+ return root < this.lineCount ? this.lineStarts[root] : this.n;
1382
+ }
1383
+ /** The block (so far) containing offset `pos`, or null. */
1384
+ blockAt(pos) {
1385
+ let lo = 0;
1386
+ let hi = this.blocks.length - 1;
1387
+ while (lo <= hi) {
1388
+ const mid = lo + hi >>> 1;
1389
+ const block = this.blocks[mid];
1390
+ if (pos < block.start) hi = mid - 1;
1391
+ else if (pos >= block.end) lo = mid + 1;
1392
+ else return block;
1393
+ }
1394
+ return null;
1395
+ }
1272
1396
  push(block) {
1273
1397
  if (block.end <= block.start) return;
1274
1398
  if (block.kind === "prose" && this.isBlank(block.start, block.end)) {
@@ -1472,7 +1596,10 @@ var Tokenizer = class {
1472
1596
  continue;
1473
1597
  }
1474
1598
  if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
1475
- if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
1599
+ if (FOOTNOTE_DEF.test(line.trimStart())) break;
1600
+ const block = this.detectBlock(this.lineStarts[k], k, false);
1601
+ if (block && this.mayDissolve(block)) return this.heldFootnote(p, open[1]);
1602
+ if (block) break;
1476
1603
  last = k;
1477
1604
  }
1478
1605
  const end = this.contentEnds[last];
@@ -1488,7 +1615,8 @@ var Tokenizer = class {
1488
1615
  return {
1489
1616
  count: this.lineCount,
1490
1617
  line: (k) => this.lineText(k),
1491
- openTail: (k) => this.isOpenTail(k)
1618
+ openTail: (k) => this.isOpenTail(k),
1619
+ streaming: this.streaming
1492
1620
  };
1493
1621
  }
1494
1622
  /**
@@ -1548,6 +1676,10 @@ var Tokenizer = class {
1548
1676
  if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
1549
1677
  const inner = this.detectBlockUncached(start, j, false);
1550
1678
  if (!inner) continue;
1679
+ if (this.streaming && inner.end >= tildeEnd && this.paragraphMayGrow(inner.end)) {
1680
+ this.overflowComplete = false;
1681
+ return this.n;
1682
+ }
1551
1683
  if (inner.end > end) {
1552
1684
  end = inner.end;
1553
1685
  this.overflowComplete = inner.complete;
@@ -1676,12 +1808,7 @@ var Tokenizer = class {
1676
1808
  return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
1677
1809
  }
1678
1810
  const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
1679
- const loneTag = atParaStart && t.trimEnd() === tag.raw;
1680
- if (loneTag && this.isOpenTail(li)) {
1681
- this.provisionalFrom = Math.min(this.provisionalFrom, p);
1682
- return null;
1683
- }
1684
- const typeSeven = loneTag;
1811
+ const typeSeven = atParaStart && t.trimEnd() === tag.raw;
1685
1812
  if (!typeSix && !typeSeven) return null;
1686
1813
  let end = this.contentEnds[li];
1687
1814
  for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
@@ -1739,6 +1866,13 @@ var Tokenizer = class {
1739
1866
  }
1740
1867
  return null;
1741
1868
  }
1869
+ if (this.isOpenTail(lastLine)) {
1870
+ if (isRecognizedJsonHead(this.text.slice(tp, tp + 512))) {
1871
+ return this.island("json", p, this.n, false, jsonMeta(this.text.slice(tp)));
1872
+ }
1873
+ this.provisionalFrom = Math.min(this.provisionalFrom, p);
1874
+ return null;
1875
+ }
1742
1876
  if (net !== 0) return null;
1743
1877
  const end = this.contentEnds[lastLine];
1744
1878
  const content = this.text.slice(tp, end).trim();
@@ -1773,8 +1907,10 @@ var Tokenizer = class {
1773
1907
  let breakLine = -1;
1774
1908
  for (let k = li + 1; k < this.lineCount; k++) {
1775
1909
  if (this.lineIsBlank(k)) break;
1776
- if (this.detectBlock(this.lineStarts[k], k, false)) {
1910
+ const block = this.detectBlock(this.lineStarts[k], k, false);
1911
+ if (block) {
1777
1912
  breakLine = k;
1913
+ if (this.mayDissolve(block)) this.undecidedBreaks.add(end);
1778
1914
  break;
1779
1915
  }
1780
1916
  end = this.contentEnds[k];
@@ -1812,7 +1948,7 @@ var Tokenizer = class {
1812
1948
  this.push({ kind: "prose", start: pos, end, island: null, inlines: scan2.inlines });
1813
1949
  return end;
1814
1950
  }
1815
- const br = scan2.breakIsland;
1951
+ const br = { ...scan2.breakIsland, fromParagraph: true };
1816
1952
  if (scan2.orphan) {
1817
1953
  let start = pos;
1818
1954
  while (this.blocks.length) {
@@ -1835,6 +1971,15 @@ var Tokenizer = class {
1835
1971
  this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
1836
1972
  return br.end;
1837
1973
  }
1974
+ /**
1975
+ * An island that opens inside a paragraph and closes past its end becomes a
1976
+ * block. Streaming: while the paragraph may still grow over its closer, the
1977
+ * island may yet be inline — undecided, so it runs open to the end (F3).
1978
+ */
1979
+ spill(type, start, end, pe, meta) {
1980
+ if (this.paragraphMayGrow(pe)) return this.island(type, start, this.n, false, meta);
1981
+ return this.island(type, start, end, true, meta);
1982
+ }
1838
1983
  scanInline(ps, pe) {
1839
1984
  const text = this.text;
1840
1985
  const inlines = [];
@@ -1869,6 +2014,9 @@ var Tokenizer = class {
1869
2014
  i = close + 2;
1870
2015
  continue;
1871
2016
  }
2017
+ if (close === -1 && this.streaming && this.paragraphMayGrow(pe)) {
2018
+ return { inlines, breakIsland: null, orphan: false };
2019
+ }
1872
2020
  }
1873
2021
  i += 2;
1874
2022
  continue;
@@ -1889,17 +2037,13 @@ var Tokenizer = class {
1889
2037
  i += 2;
1890
2038
  continue;
1891
2039
  }
1892
- if (close + 2 > pe) return stop(this.island("math_block", i, close + 2, true, { delimiter: "$$" }));
2040
+ if (close + 2 > pe) return stop(this.spill("math_block", i, close + 2, pe, { delimiter: "$$" }));
1893
2041
  inlines.push(this.island("math_inline", i, close + 2, true, { delimiter: "$$" }));
1894
2042
  i = close + 2;
1895
2043
  continue;
1896
2044
  }
1897
2045
  const end = singleDollarMathEnd(text, i, pe);
1898
2046
  this.tick(end > 0 ? end - i : Math.min(403, pe - i));
1899
- if (end > 0 && this.streaming && end === this.n) {
1900
- this.provisionalFrom = Math.min(this.provisionalFrom, i);
1901
- return { inlines, breakIsland: null, orphan: false };
1902
- }
1903
2047
  if (end > 0) {
1904
2048
  inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
1905
2049
  i = end;
@@ -1977,7 +2121,7 @@ var Tokenizer = class {
1977
2121
  const anchor = end - i <= 64 ? PINNED_ANCHOR_RE.exec(text.slice(i, end)) : null;
1978
2122
  const type = anchor ? "anchor" : "html_comment";
1979
2123
  const meta = anchor ? { id: anchor[1] } : {};
1980
- if (end > pe) return stop(this.island(type, i, end, true, meta));
2124
+ if (end > pe) return stop(this.spill(type, i, end, pe, meta));
1981
2125
  inlines.push(this.island(type, i, end, true, meta));
1982
2126
  i = end;
1983
2127
  continue;
@@ -2006,7 +2150,7 @@ var Tokenizer = class {
2006
2150
  const blockType = isAttr ? "xml_attr" : "xml_region";
2007
2151
  if (close === -1) return stop(this.island(blockType, i, this.n, false, { tag: name }));
2008
2152
  const end = close + closer.length;
2009
- if (end > pe) return stop(this.island(blockType, i, end, true, { tag: name }));
2153
+ if (end > pe) return stop(this.spill(blockType, i, end, pe, { tag: name }));
2010
2154
  inlines.push(this.island("xml_inline", i, end, true, { tag: name }));
2011
2155
  i = end;
2012
2156
  continue;
@@ -2391,6 +2535,7 @@ function mapRange(changes, start, end, unit = "utf16") {
2391
2535
  return { start: newStart, end: newEnd, touched, collapsed: newEnd === newStart && end > start };
2392
2536
  }
2393
2537
 
2538
+ exports.AFTER_WINDOW = AFTER_WINDOW;
2394
2539
  exports.BacktickRuns = BacktickRuns;
2395
2540
  exports.CALLOUT_NAMES = CALLOUT_NAMES;
2396
2541
  exports.FENCE_OPENER_WHITESPACE = FENCE_OPENER_WHITESPACE;
@@ -2426,6 +2571,7 @@ exports.pairDisplayMath = pairDisplayMath;
2426
2571
  exports.parseFenceOpener = parseFenceOpener;
2427
2572
  exports.pendingDisplayMathOpener = pendingDisplayMathOpener;
2428
2573
  exports.readXmlTag = readXmlTag;
2574
+ exports.singleDollarAfter = singleDollarAfter;
2429
2575
  exports.singleDollarMathEnd = singleDollarMathEnd;
2430
2576
  exports.spliceSave = spliceSave;
2431
2577
  exports.splitsSurrogatePair = splitsSurrogatePair;