@ai-matrx/content-ir 0.13.0 → 0.14.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
@@ -211,11 +211,22 @@ var NESTING_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
211
211
  function fenceNestsInnerFences(language) {
212
212
  return !!language && NESTING_FENCE_LANGUAGES.has(language.toLowerCase());
213
213
  }
214
+ var FENCE_WHITESPACE = " \n\r\f\v";
215
+ function isFenceWhitespace(ch) {
216
+ return ch !== void 0 && ch !== "" && FENCE_WHITESPACE.includes(ch);
217
+ }
218
+ function trimFenceLine(line) {
219
+ let start = 0;
220
+ let end = line.length;
221
+ while (start < end && isFenceWhitespace(line[start])) start++;
222
+ while (end > start && isFenceWhitespace(line[end - 1])) end--;
223
+ return start === 0 && end === line.length ? line : line.slice(start, end);
224
+ }
214
225
  function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
215
226
  let ticks = 0;
216
227
  while (ticks < trimmed.length && trimmed[ticks] === "`") ticks++;
217
228
  if (ticks < 3) return "content";
218
- const info = trimmed.slice(ticks).trim();
229
+ const info = trimFenceLine(trimmed.slice(ticks));
219
230
  if (info === "") {
220
231
  if (ticks < openTicks) return "content";
221
232
  if (nests && nestedDepth > 0) return "close-nested";
@@ -228,13 +239,43 @@ function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
228
239
  // source/math.ts
229
240
  var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
230
241
  var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
242
+ var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
243
+ var FUNCTION_NAMES = /* @__PURE__ */ new Set([
244
+ "sin",
245
+ "cos",
246
+ "tan",
247
+ "sec",
248
+ "csc",
249
+ "cot",
250
+ "log",
251
+ "ln",
252
+ "exp",
253
+ "lim",
254
+ "max",
255
+ "min",
256
+ "det",
257
+ "gcd",
258
+ "mod",
259
+ "arg",
260
+ "sinh",
261
+ "cosh",
262
+ "tanh"
263
+ ]);
264
+ function isMathShape(content) {
265
+ if (!MATH_SHAPE_CHARS.test(content)) return false;
266
+ const words = content.match(/[A-Za-z]+/g);
267
+ if (!words) return false;
268
+ if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
269
+ if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
270
+ return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
271
+ }
231
272
  function isSingleDollarMath(content, after) {
232
273
  if (!content || content.length > 400) return false;
233
274
  if (/^\s/.test(content) || /\s$/.test(content)) return false;
234
275
  if (after !== void 0 && /[0-9]/.test(after)) return false;
235
276
  if (LONE_VARIABLE.test(content)) return true;
236
277
  if (/^[0-9]/.test(content)) return /\\[A-Za-z]+|[\\^_{}]/.test(content);
237
- return TEX_SIGNAL.test(content);
278
+ return TEX_SIGNAL.test(content) || isMathShape(content);
238
279
  }
239
280
  function singleDollarMathEnd(text, open, limit = text.length) {
240
281
  const bound = Math.min(limit, open + 403);
@@ -470,6 +511,10 @@ var XmlContainerTracker = class {
470
511
 
471
512
  // source/tokenize.ts
472
513
  var NOT_WHITESPACE = /\S/;
514
+ var DIRECTIVE_OPEN = /^(:{2,})([A-Za-z][\w-]*)/;
515
+ var MKDOCS_OPEN = /^(!!!|\?\?\?\+?)[ \t]+([A-Za-z][\w-]*)/;
516
+ var CALLOUT_OPEN = /^>[ \t]*\[!([A-Za-z][\w-]*)\]([+-])?/;
517
+ var FOOTNOTE_DEF = /^\[\^([^\]\s]+)\]:/;
473
518
  function countStructuralBraces(source) {
474
519
  let opens = 0;
475
520
  let closes = 0;
@@ -597,8 +642,9 @@ function isSplitterTreeLine(line) {
597
642
  }
598
643
  var MEDIA_REF_PREFIXES = ["[Image URL:", "[Video URL:", "[Audio URL:"];
599
644
  var Tokenizer = class {
600
- constructor(text) {
645
+ constructor(text, streaming = false) {
601
646
  this.text = text;
647
+ this.streaming = streaming;
602
648
  this.n = text.length;
603
649
  this.mathPairs = pairDisplayMath(text);
604
650
  let start = 0;
@@ -617,6 +663,7 @@ var Tokenizer = class {
617
663
  }
618
664
  }
619
665
  text;
666
+ streaming;
620
667
  n;
621
668
  lineStarts = [];
622
669
  contentEnds = [];
@@ -654,6 +701,10 @@ var Tokenizer = class {
654
701
  }
655
702
  return lo < list.length ? list[lo] : -1;
656
703
  }
704
+ /** Streaming only: the final line, still waiting for its terminator. */
705
+ isOpenTail(k) {
706
+ return this.streaming && k === this.lineStarts.length - 1 && this.lineEnds[k] === this.contentEnds[k];
707
+ }
657
708
  get lineCount() {
658
709
  return this.lineStarts.length;
659
710
  }
@@ -735,6 +786,22 @@ var Tokenizer = class {
735
786
  const fm = this.frontMatter(li, t);
736
787
  if (fm) return fm;
737
788
  }
789
+ if (indent <= 3 && t.startsWith("::")) {
790
+ const directive = this.directive(p, li, t);
791
+ if (directive) return directive;
792
+ }
793
+ if (indent === 0 && (t.startsWith("!!!") || t.startsWith("???"))) {
794
+ const admonition = this.mkdocsAdmonition(p, li, t);
795
+ if (admonition) return admonition;
796
+ }
797
+ if (indent <= 3 && t.startsWith(">")) {
798
+ const callout = this.callout(p, li, t);
799
+ if (callout) return callout;
800
+ }
801
+ if (atParaStart && indent <= 3 && t.startsWith("[^")) {
802
+ const footnote = this.footnoteDefinition(p, li, t);
803
+ if (footnote) return footnote;
804
+ }
738
805
  if (t.startsWith("```")) return this.backtickFence(p, li, t);
739
806
  if (indent <= 3 && t.startsWith("~~~")) {
740
807
  const tilde = this.tildeFence(p, li, t);
@@ -772,19 +839,110 @@ var Tokenizer = class {
772
839
  return { type, start, end, complete, meta };
773
840
  }
774
841
  frontMatter(li, t) {
775
- if (this.lineText(li) !== "---" || t !== "---") return null;
842
+ const fence = this.lineText(li);
843
+ if (fence !== "---" && fence !== "+++" || t !== fence) return null;
844
+ const toml = fence === "+++";
776
845
  let sawContent = false;
777
846
  for (let k = li + 1; k < this.lineCount; k++) {
778
847
  const line = this.lineText(k);
779
- if (line === "---" || line === "...") {
780
- return sawContent ? this.island("front_matter", 0, this.contentEnds[k], true) : null;
848
+ if (line === fence || !toml && line === "...") {
849
+ return sawContent ? this.island("front_matter", 0, this.contentEnds[k], true, { format: toml ? "toml" : "yaml" }) : null;
781
850
  }
782
851
  if (!NOT_WHITESPACE.test(line)) continue;
783
- if (!/^(?:[A-Za-z0-9_"'-][^:]*:(?:\s|$)|\s+\S|-\s)/.test(line)) return null;
852
+ const shaped = toml ? /^\s*(?:[A-Za-z0-9_."-]+\s*=|\[|#)/.test(line) : /^(?:[A-Za-z0-9_"'-][^:]*:(?:\s|$)|\s+\S|-\s|#)/.test(line);
853
+ if (!shaped) return null;
784
854
  sawContent = true;
785
855
  }
786
856
  return null;
787
857
  }
858
+ /**
859
+ * `:::name[label]{attrs}` … closing `:::` (a colon run at least as long as
860
+ * the opener's, nesting counted), or a leaf `::name[…]{…}` line. The whole
861
+ * container is ONE island: its grammar (fence lengths, labels, attributes,
862
+ * nested tabs) is exactly what a visual editor would re-serialize wrongly.
863
+ */
864
+ directive(p, li, t) {
865
+ const open = DIRECTIVE_OPEN.exec(t);
866
+ if (!open) return null;
867
+ const colons = open[1].length;
868
+ const name = open[2];
869
+ if (colons === 2) {
870
+ return this.island("directive", p, this.contentEnds[li], true, { name, leaf: true });
871
+ }
872
+ const stack = [colons];
873
+ for (let k = li + 1; k < this.lineCount; k++) {
874
+ const line = this.lineText(k).trim();
875
+ const inner = DIRECTIVE_OPEN.exec(line);
876
+ if (inner && inner[1].length >= 3) {
877
+ stack.push(inner[1].length);
878
+ continue;
879
+ }
880
+ const close = /^(:{3,})\s*$/.exec(line);
881
+ if (close && close[1].length >= stack[stack.length - 1]) {
882
+ stack.pop();
883
+ if (stack.length === 0) return this.island("directive", p, this.contentEnds[k], true, { name });
884
+ }
885
+ }
886
+ return this.island("directive", p, this.n, false, { name });
887
+ }
888
+ /** MkDocs `!!! type "Title"` / `??? type` / `???+ type` and its indented (or blank-separated indented) body. */
889
+ mkdocsAdmonition(p, li, t) {
890
+ const open = MKDOCS_OPEN.exec(t);
891
+ if (!open) return null;
892
+ let last = li;
893
+ for (let k = li + 1; k < this.lineCount; k++) {
894
+ const line = this.lineText(k);
895
+ if (/^(?: {4}|\t)/.test(line) && NOT_WHITESPACE.test(line)) {
896
+ last = k;
897
+ continue;
898
+ }
899
+ if (!NOT_WHITESPACE.test(line)) continue;
900
+ break;
901
+ }
902
+ return this.island("directive", p, this.contentEnds[last], true, {
903
+ name: open[2].toLowerCase(),
904
+ syntax: "mkdocs"
905
+ });
906
+ }
907
+ /** A blockquote whose first line is `> [!TYPE]…` — every following `>` line belongs to it. */
908
+ callout(p, li, t) {
909
+ const open = CALLOUT_OPEN.exec(t);
910
+ if (!open) return null;
911
+ let last = li;
912
+ for (let k = li + 1; k < this.lineCount; k++) {
913
+ if (!/^ {0,3}>/.test(this.lineText(k))) break;
914
+ last = k;
915
+ }
916
+ return this.island("callout", p, this.contentEnds[last], true, {
917
+ name: open[1].toLowerCase(),
918
+ ...open[2] ? { fold: open[2] === "-" ? "closed" : "open" } : {}
919
+ });
920
+ }
921
+ /** `[^id]: text` plus its continuation lines (indented, or lazy text before a blank line). */
922
+ footnoteDefinition(p, li, t) {
923
+ const open = FOOTNOTE_DEF.exec(t);
924
+ if (!open) return null;
925
+ let last = li;
926
+ for (let k = li + 1; k < this.lineCount; k++) {
927
+ const line = this.lineText(k);
928
+ if (!NOT_WHITESPACE.test(line)) {
929
+ let j = k + 1;
930
+ while (j < this.lineCount && !NOT_WHITESPACE.test(this.lineText(j))) j++;
931
+ if (j < this.lineCount && /^(?: {4}|\t)/.test(this.lineText(j))) {
932
+ k = j - 1;
933
+ continue;
934
+ }
935
+ break;
936
+ }
937
+ if (/^(?: {4}|\t)/.test(line)) {
938
+ last = k;
939
+ continue;
940
+ }
941
+ if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
942
+ last = k;
943
+ }
944
+ return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
945
+ }
788
946
  /**
789
947
  * Backtick fence — the renderer splitter's close rules: JSON-string aware for
790
948
  * ```json, and THE nested-fence rule (source/fence-nesting.ts) for markdown
@@ -813,8 +971,9 @@ var Tokenizer = class {
813
971
  let nestedDepth = 0;
814
972
  let sawNested = false;
815
973
  for (let k = li + 1; k < this.lineCount; k++) {
974
+ if (this.isOpenTail(k)) continue;
816
975
  const line = this.lineText(k);
817
- const trimmedLine = line.trim();
976
+ const trimmedLine = trimFenceLine(line);
818
977
  if (trimmedLine.startsWith("```")) {
819
978
  const at2 = line.indexOf("```");
820
979
  if (isJson) {
@@ -844,7 +1003,7 @@ var Tokenizer = class {
844
1003
  }
845
1004
  }
846
1005
  const closeTicks = backtickRunLength(line, at);
847
- const after = line.slice(at + closeTicks).trim();
1006
+ const after = trimFenceLine(line.slice(at + closeTicks));
848
1007
  if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
849
1008
  if (isJson) state = jsonStringState(line, 0, line.length, state);
850
1009
  continue;
@@ -871,6 +1030,7 @@ var Tokenizer = class {
871
1030
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
872
1031
  const close = new RegExp(`^ {0,3}~{${ticks},}[ \\t]*$`);
873
1032
  for (let k = li + 1; k < this.lineCount; k++) {
1033
+ if (this.isOpenTail(k)) continue;
874
1034
  if (close.test(this.lineText(k))) {
875
1035
  return this.island("fence", p, this.contentEnds[k], true, meta);
876
1036
  }
@@ -1260,6 +1420,21 @@ var Tokenizer = class {
1260
1420
  i++;
1261
1421
  continue;
1262
1422
  }
1423
+ if (ch === "[" && text[i + 1] === "[" || ch === "!" && text[i + 1] === "[" && text[i + 2] === "[") {
1424
+ const open = ch === "!" ? i + 3 : i + 2;
1425
+ const close = within("]]", open);
1426
+ const newline = this.next("\n", open);
1427
+ const inner = close === -1 ? "" : text.slice(open, close);
1428
+ if (close !== -1 && (newline === -1 || newline > close) && inner.trim() && !/[[\]]/.test(inner)) {
1429
+ const cut = inner.search(/\\?\|/);
1430
+ const target = (cut < 0 ? inner : inner.slice(0, cut)).trim();
1431
+ inlines.push(
1432
+ this.island("wikilink", i, close + 2, true, ch === "!" ? { target, embed: true } : { target })
1433
+ );
1434
+ i = close + 2;
1435
+ continue;
1436
+ }
1437
+ }
1263
1438
  if (ch === "[") {
1264
1439
  const media = MEDIA_REF_PREFIXES.find((prefix) => text.startsWith(prefix, i));
1265
1440
  if (media) {
@@ -1341,8 +1516,8 @@ var Tokenizer = class {
1341
1516
  return { inlines, breakIsland: null, orphan: false };
1342
1517
  }
1343
1518
  };
1344
- function tokenizeSource(text) {
1345
- const raw = new Tokenizer(text).run();
1519
+ function tokenizeSource(text, options = {}) {
1520
+ const raw = new Tokenizer(text, options.streaming ?? false).run();
1346
1521
  const cp = buildCodePointIndex(text);
1347
1522
  const inline = (island) => ({
1348
1523
  islandType: island.type,
@@ -1391,7 +1566,7 @@ function listIslands(blocks) {
1391
1566
  raw: block.raw
1392
1567
  });
1393
1568
  } else {
1394
- out.push(...block.inlines);
1569
+ for (const inline of block.inlines) out.push(inline);
1395
1570
  }
1396
1571
  }
1397
1572
  return out;
@@ -1427,15 +1602,37 @@ function islandEdit(island, text) {
1427
1602
  function blockRangeEdit(first, last, text) {
1428
1603
  return { start: first.start, end: last.end, text };
1429
1604
  }
1430
- function locateIsland(text, raw, from, expected, expectedFromEnd) {
1431
- if (expected >= from && text.startsWith(raw, expected)) return expected;
1432
- if (expectedFromEnd >= from && text.startsWith(raw, expectedFromEnd)) return expectedFromEnd;
1605
+ var CONTEXT_WINDOW = 64;
1606
+ function locateIsland(text, raw, from, expected, expectedFromEnd, oldText, oldStart, oldEnd) {
1607
+ const candidates = /* @__PURE__ */ new Set();
1608
+ if (expected >= from && text.startsWith(raw, expected)) candidates.add(expected);
1609
+ if (expectedFromEnd >= from && text.startsWith(raw, expectedFromEnd)) candidates.add(expectedFromEnd);
1433
1610
  const after = text.indexOf(raw, Math.max(from, expected));
1611
+ if (after !== -1) candidates.add(after);
1434
1612
  const before = expected > from ? text.lastIndexOf(raw, expected) : -1;
1435
- const beforeOk = before >= from ? before : -1;
1436
- if (after === -1) return beforeOk;
1437
- if (beforeOk === -1) return after;
1438
- return expected - beforeOk <= after - expected ? beforeOk : after;
1613
+ if (before >= from) candidates.add(before);
1614
+ if (candidates.size === 0) return text.indexOf(raw, from);
1615
+ if (candidates.size === 1) return candidates.values().next().value;
1616
+ let best = -1;
1617
+ let bestScore = -1;
1618
+ for (const at of candidates) {
1619
+ let score = 0;
1620
+ for (let k = 0; k < CONTEXT_WINDOW; k++) {
1621
+ const n = text[at + raw.length + k];
1622
+ if (n === void 0 || n !== oldText[oldEnd + k]) break;
1623
+ score++;
1624
+ }
1625
+ for (let k = 1; k <= CONTEXT_WINDOW; k++) {
1626
+ const n = text[at - k];
1627
+ if (n === void 0 || n !== oldText[oldStart - k]) break;
1628
+ score++;
1629
+ }
1630
+ if (score > bestScore || score === bestScore && at === expected) {
1631
+ best = at;
1632
+ bestScore = score;
1633
+ }
1634
+ }
1635
+ return best;
1439
1636
  }
1440
1637
  function commonPrefix(a, b) {
1441
1638
  const max = Math.min(a.length, b.length);
@@ -1512,7 +1709,16 @@ function spliceSave(original, edits, options = {}) {
1512
1709
  let oldCursor2 = edit.start;
1513
1710
  let newCursor2 = 0;
1514
1711
  for (const island of inside) {
1515
- const at = locateIsland(edit.text, island.raw, newCursor2, newCursor2 + (island.start - oldCursor2), edit.text.length - (edit.end - island.start));
1712
+ const at = locateIsland(
1713
+ edit.text,
1714
+ island.raw,
1715
+ newCursor2,
1716
+ newCursor2 + (island.start - oldCursor2),
1717
+ edit.text.length - (edit.end - island.start),
1718
+ original.slice(edit.start, edit.end),
1719
+ island.start - edit.start,
1720
+ island.end - edit.start
1721
+ );
1516
1722
  if (at === -1) {
1517
1723
  const name = island.meta.tag ?? island.meta.name ?? island.meta.lang ?? "";
1518
1724
  throw new SourceSpliceError(
@@ -1650,6 +1856,7 @@ function mapRange(changes, start, end, unit = "utf16") {
1650
1856
  return { start: newStart, end: newEnd, touched, collapsed: newEnd === newStart && end > start };
1651
1857
  }
1652
1858
 
1859
+ exports.FENCE_WHITESPACE = FENCE_WHITESPACE;
1653
1860
  exports.NESTING_FENCE_LANGUAGES = NESTING_FENCE_LANGUAGES;
1654
1861
  exports.SourceSpliceError = SourceSpliceError;
1655
1862
  exports.XmlContainerTracker = XmlContainerTracker;
@@ -1674,5 +1881,6 @@ exports.splitsSurrogatePair = splitsSurrogatePair;
1674
1881
  exports.toCodePointOffset = toCodePointOffset;
1675
1882
  exports.toUtf16Offset = toUtf16Offset;
1676
1883
  exports.tokenizeSource = tokenizeSource;
1884
+ exports.trimFenceLine = trimFenceLine;
1677
1885
  //# sourceMappingURL=source.cjs.map
1678
1886
  //# sourceMappingURL=source.cjs.map