@ai-matrx/content-ir 0.16.1 → 0.17.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
@@ -315,12 +315,167 @@ function singleDollarMathEnd(text, open, limit = text.length) {
315
315
  return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
316
316
  }
317
317
 
318
+ // source/code-ranges.ts
319
+ function findCodeRanges(text) {
320
+ const ranges = [];
321
+ const n = text.length;
322
+ let fence = null;
323
+ let para = -1;
324
+ const spans = (from, to) => {
325
+ const runs = [];
326
+ for (let k = from; k < to; ) {
327
+ if (text.charCodeAt(k) !== 96) {
328
+ k++;
329
+ continue;
330
+ }
331
+ let len = 1;
332
+ while (k + len < to && text.charCodeAt(k + len) === 96) len++;
333
+ runs.push([k, len]);
334
+ k += len;
335
+ }
336
+ const nextSame = new Array(runs.length).fill(-1);
337
+ const last = /* @__PURE__ */ new Map();
338
+ for (let r = runs.length - 1; r >= 0; r--) {
339
+ const len = runs[r][1];
340
+ nextSame[r] = last.get(len) ?? -1;
341
+ last.set(len, r);
342
+ }
343
+ for (let r = 0; r < runs.length; ) {
344
+ const j = nextSame[r];
345
+ if (j === -1) {
346
+ r++;
347
+ continue;
348
+ }
349
+ const [s0] = runs[r];
350
+ const [s1, l1] = runs[j];
351
+ ranges.push({ start: s0, end: s1 + l1, kind: "span" });
352
+ r = j + 1;
353
+ }
354
+ };
355
+ let pos = 0;
356
+ while (pos <= n) {
357
+ const nl = text.indexOf("\n", pos);
358
+ const lineEnd = nl === -1 ? n : nl;
359
+ const line = text.slice(pos, lineEnd);
360
+ let indent = 0;
361
+ while (indent < line.length && line[indent] === " ") indent++;
362
+ const body = line.slice(indent);
363
+ if (fence) {
364
+ const trimmed = trimFenceLine(line);
365
+ let closes = false;
366
+ if (fence.char === "`") {
367
+ const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
368
+ if (kind === "open-nested") fence.depth++;
369
+ else if (kind === "close-nested") fence.depth--;
370
+ else if (kind === "close-outer") closes = indent <= 3;
371
+ } else {
372
+ let t = 0;
373
+ while (t < trimmed.length && trimmed[t] === "~") t++;
374
+ closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
375
+ }
376
+ if (closes) {
377
+ ranges.push({ start: fence.start, end: lineEnd, kind: "fence" });
378
+ fence = null;
379
+ }
380
+ } else {
381
+ const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
382
+ const info = opener ? opener[2] : "";
383
+ if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
384
+ if (para !== -1) spans(para, pos);
385
+ para = -1;
386
+ const char = opener[1][0];
387
+ const parsed = char === "`" ? parseFenceOpener(line) : null;
388
+ fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
389
+ } else if (body.trim() === "") {
390
+ if (para !== -1) spans(para, pos);
391
+ para = -1;
392
+ } else if (para === -1) para = pos;
393
+ }
394
+ if (nl === -1) break;
395
+ pos = nl + 1;
396
+ }
397
+ if (fence) ranges.push({ start: fence.start, end: n, kind: "fence" });
398
+ else if (para !== -1) spans(para, n);
399
+ return ranges.sort((a, b) => a.start - b.start);
400
+ }
401
+ function pendingCodeSpanStart(text) {
402
+ const n = text.length;
403
+ if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
404
+ let fence = null;
405
+ let para = -1;
406
+ let pos = 0;
407
+ while (pos <= n) {
408
+ const nl = text.indexOf("\n", pos);
409
+ const lineEnd = nl === -1 ? n : nl;
410
+ const line = text.slice(pos, lineEnd);
411
+ let indent = 0;
412
+ while (indent < line.length && line[indent] === " ") indent++;
413
+ const body = line.slice(indent);
414
+ if (fence) {
415
+ const trimmed = trimFenceLine(line);
416
+ let closes = false;
417
+ if (fence.char === "`") {
418
+ const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
419
+ if (kind === "open-nested") fence.depth++;
420
+ else if (kind === "close-nested") fence.depth--;
421
+ else if (kind === "close-outer") closes = indent <= 3;
422
+ } else {
423
+ let t = 0;
424
+ while (t < trimmed.length && trimmed[t] === "~") t++;
425
+ closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
426
+ }
427
+ if (closes) fence = null;
428
+ } else {
429
+ const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
430
+ const info = opener ? opener[2] : "";
431
+ if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
432
+ para = -1;
433
+ const char = opener[1][0];
434
+ const parsed = char === "`" ? parseFenceOpener(line) : null;
435
+ fence = { char, len: opener[1].length, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
436
+ } else if (body.trim() === "") {
437
+ if (!(nl === -1 && line === "")) para = -1;
438
+ } else if (para === -1) para = pos;
439
+ }
440
+ if (nl === -1) break;
441
+ pos = nl + 1;
442
+ }
443
+ if (fence || para === -1) return -1;
444
+ const runs = [];
445
+ for (let k = para; k < n; ) {
446
+ if (text.charCodeAt(k) !== 96) {
447
+ k++;
448
+ continue;
449
+ }
450
+ let len = 1;
451
+ while (k + len < n && text.charCodeAt(k + len) === 96) len++;
452
+ runs.push([k, len]);
453
+ k += len;
454
+ }
455
+ const growing = (r) => {
456
+ const [start, len] = runs[r];
457
+ return start + len === n;
458
+ };
459
+ for (let r = 0; r < runs.length; ) {
460
+ if (growing(r)) return runs[r][0];
461
+ const len = runs[r][1];
462
+ let j = r + 1;
463
+ while (j < runs.length && (runs[j][1] !== len || growing(j))) j++;
464
+ if (j >= runs.length) return runs[r][0];
465
+ r = j + 1;
466
+ }
467
+ return -1;
468
+ }
469
+
318
470
  // source/math-pairs.ts
319
471
  var MAX_MATH_SPAN = 600;
320
472
  var STRUCTURAL_MARKDOWN = /\]\(|https?:\/\/|\*\*|(?:^|\n)[ \t]{0,3}#{1,6}[ \t]|(?:^|\n)[ \t]*[-*+][ \t]+|(?:^|\n)[ \t]*\d+[.)][ \t]/;
321
473
  var LATEX_COMMAND = /\\[a-zA-Z]/;
322
474
  var PROSE_WORD = /[A-Za-z]{3,}/g;
323
475
  var PROSE_WORD_LIMIT = 6;
476
+ function isMathPlaceholder(inner) {
477
+ return /^[\s.…·⋯_-]*$/.test(inner) && /[.…·⋯]/.test(inner);
478
+ }
324
479
  function opensFlowBlock(text, open) {
325
480
  for (let k = open - 1; k >= 0 && text[k] !== "\n"; k--) if (text[k] !== " " && text[k] !== " ") return false;
326
481
  for (let k = open + 2; k < text.length && text[k] !== "\n"; k++) if (text[k] !== " " && text[k] !== " " && text[k] !== "\r") return false;
@@ -338,15 +493,7 @@ function looksLikeDisplayMath(inner, flow = false) {
338
493
  return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
339
494
  }
340
495
  function codeRanges(text) {
341
- const ranges = [];
342
- for (const re of [/```[\s\S]*?(?:```|$)/g, /~~~[\s\S]*?(?:~~~|$)/g, /`[^`\n]*`/g]) {
343
- let m;
344
- while ((m = re.exec(text)) !== null) {
345
- ranges.push([m.index, m.index + m[0].length]);
346
- if (m[0].length === 0) re.lastIndex++;
347
- }
348
- }
349
- return ranges.sort((a, b) => a[0] - b[0]);
496
+ return findCodeRanges(text).map((r) => [r.start, r.end]);
350
497
  }
351
498
  function pendingDisplayMathOpener(text) {
352
499
  if (!text.includes("$$")) return -1;
@@ -368,6 +515,10 @@ function pendingDisplayMathOpener(text) {
368
515
  const open = tokens[j];
369
516
  const close = tokens[j + 1];
370
517
  if (close === void 0) break;
518
+ if (isMathPlaceholder(text.slice(open + 2, close))) {
519
+ j += 2;
520
+ continue;
521
+ }
371
522
  if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
372
523
  paired.add(open);
373
524
  paired.add(close);
@@ -412,6 +563,10 @@ function pairDisplayMath(text) {
412
563
  const open = tokens[j];
413
564
  const close = tokens[j + 1];
414
565
  if (close === void 0) break;
566
+ if (isMathPlaceholder(text.slice(open + 2, close))) {
567
+ j += 2;
568
+ continue;
569
+ }
415
570
  if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
416
571
  pairs.set(open, close);
417
572
  j += 2;
@@ -749,6 +904,27 @@ var Tokenizer = class {
749
904
  this.mathPairs = pairDisplayMath(text);
750
905
  this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
751
906
  if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
907
+ if (streaming && text.includes("$$")) {
908
+ const span = pendingCodeSpanStart(text);
909
+ if (span !== -1) {
910
+ const firstToken = text.indexOf("$$", span);
911
+ if (firstToken !== -1) this.provisionalFrom = Math.min(this.provisionalFrom, firstToken);
912
+ for (const [open, close] of this.mathPairs) {
913
+ if (close >= span) this.provisionalFrom = Math.min(this.provisionalFrom, open);
914
+ }
915
+ }
916
+ for (const pad of ["`", "``", "```", "~~~"]) {
917
+ const alt = pairDisplayMath(text + pad);
918
+ for (const [open, close] of this.mathPairs) {
919
+ if (alt.get(open) !== close) this.provisionalFrom = Math.min(this.provisionalFrom, open);
920
+ }
921
+ for (const [open, close] of alt) {
922
+ if (close < text.length && this.mathPairs.get(open) !== close) {
923
+ this.provisionalFrom = Math.min(this.provisionalFrom, open);
924
+ }
925
+ }
926
+ }
927
+ }
752
928
  let start = 0;
753
929
  for (let i = 0; i < text.length; i++) {
754
930
  if (text.charCodeAt(i) === 10) {
@@ -789,13 +965,17 @@ var Tokenizer = class {
789
965
  /**
790
966
  * Streaming: an open last line that begins with `{` is undecided — each new
791
967
  * 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).
968
+ * prose and a JSON island — and so is a line that is exactly one allowed
969
+ * HTML tag so far; both are provisional until they end (G5).
793
970
  */
794
971
  markOpenJsonTail() {
795
972
  const last = this.lineStarts.length - 1;
796
973
  if (!this.isOpenTail(last)) return;
797
974
  const start = this.lineStarts[last];
798
- if (this.lineText(last).trimStart().startsWith("{")) {
975
+ const t = this.lineText(last).trim();
976
+ const oneTag = t.startsWith("<") ? readXmlTag(t, 0) : null;
977
+ const loneTag = !!oneTag && oneTag.raw === t && ALLOWED_RAW_HTML_TAGS.has(oneTag.tagName.toLowerCase());
978
+ if (t.startsWith("{") || loneTag) {
799
979
  this.provisionalFrom = Math.min(this.provisionalFrom, start);
800
980
  }
801
981
  }
@@ -1088,11 +1268,17 @@ var Tokenizer = class {
1088
1268
  last = k;
1089
1269
  continue;
1090
1270
  }
1271
+ if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
1091
1272
  if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
1092
- if (this.isOpenTail(k)) break;
1093
1273
  last = k;
1094
1274
  }
1095
- return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
1275
+ const end = this.contentEnds[last];
1276
+ if (this.paragraphMayGrow(end)) return this.heldFootnote(p, open[1]);
1277
+ return this.island("footnote_def", p, end, true, { id: open[1] });
1278
+ }
1279
+ /** A footnote whose continuation is still arriving: open to the end of the text. */
1280
+ heldFootnote(p, id) {
1281
+ return this.island("footnote_def", p, this.n, false, { id });
1096
1282
  }
1097
1283
  /**
1098
1284
  * Backtick fence — the renderer splitter's close rules: JSON-string aware for
@@ -1336,7 +1522,12 @@ var Tokenizer = class {
1336
1522
  return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
1337
1523
  }
1338
1524
  const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
1339
- const typeSeven = atParaStart && t.trimEnd() === tag.raw && !this.isOpenTail(li);
1525
+ const loneTag = atParaStart && t.trimEnd() === tag.raw;
1526
+ if (loneTag && this.isOpenTail(li)) {
1527
+ this.provisionalFrom = Math.min(this.provisionalFrom, p);
1528
+ return null;
1529
+ }
1530
+ const typeSeven = loneTag;
1340
1531
  if (!typeSix && !typeSeven) return null;
1341
1532
  let end = this.contentEnds[li];
1342
1533
  for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
@@ -1428,10 +1619,6 @@ var Tokenizer = class {
1428
1619
  let breakLine = -1;
1429
1620
  for (let k = li + 1; k < this.lineCount; k++) {
1430
1621
  if (this.lineIsBlank(k)) break;
1431
- if (this.lineStarts[k] >= this.provisionalFrom) {
1432
- end = this.n;
1433
- break;
1434
- }
1435
1622
  if (this.detectBlock(this.lineStarts[k], k, false)) {
1436
1623
  breakLine = k;
1437
1624
  break;
@@ -1574,7 +1761,11 @@ var Tokenizer = class {
1574
1761
  }
1575
1762
  const end = singleDollarMathEnd(text, i, pe);
1576
1763
  this.tick(end > 0 ? end - i : Math.min(403, pe - i));
1577
- if (end > 0 && !(this.streaming && end === this.n)) {
1764
+ if (end > 0 && this.streaming && end === this.n) {
1765
+ this.provisionalFrom = Math.min(this.provisionalFrom, i);
1766
+ return { inlines, breakIsland: null, orphan: false };
1767
+ }
1768
+ if (end > 0) {
1578
1769
  inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
1579
1770
  i = end;
1580
1771
  continue;
@@ -2077,6 +2268,8 @@ exports.blockRangeEdit = blockRangeEdit;
2077
2268
  exports.buildCodePointIndex = buildCodePointIndex;
2078
2269
  exports.classifyInnerFenceLine = classifyInnerFenceLine;
2079
2270
  exports.fenceNestsInnerFences = fenceNestsInnerFences;
2271
+ exports.findCodeRanges = findCodeRanges;
2272
+ exports.isMathPlaceholder = isMathPlaceholder;
2080
2273
  exports.isSingleDollarMath = isSingleDollarMath;
2081
2274
  exports.islandEdit = islandEdit;
2082
2275
  exports.joinSource = joinSource;