@ai-matrx/content-ir 0.16.0 → 0.16.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.2
4
+
5
+ - **Fixed (`/source` `pairDisplayMath`, `pendingDisplayMathOpener`):** a
6
+ `$$...$$` / `$$…$$` placeholder in prose was paired as display math. New
7
+ `isMathPlaceholder(inner)`; such a pair is consumed whole as literal text.
8
+ Same rule as `@ai-matrx/kit` `delimiter-guard`.
9
+ - Consumer action: none — refresh to latest.
10
+
11
+ ## 0.16.1
12
+
13
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
14
+ `git diff npm/content-ir-core/v0.16.0..npm/content-ir-core/v0.16.1 -- apps/shared/content-ir-core`).
15
+ No source changes intended and no consumer action required.
16
+
17
+ ## Unreleased
18
+
19
+ **`/source` — a callout or `:::` directive nested in a list item stays in the
20
+ list's prose block** (verify-RC-B8 round 2, SAFETY). Guard:
21
+ `source-markdown-syntax-islands.test.ts` (3 cases red before).
22
+
23
+ **Consumer action:** none.
24
+
3
25
  ## 0.16.0 — 2026-09-25
4
26
 
5
27
  **`/source` — a deterministic attack suite, and streaming that only ever
package/dist/index.cjs CHANGED
@@ -5294,6 +5294,9 @@ var STRUCTURAL_MARKDOWN = /\]\(|https?:\/\/|\*\*|(?:^|\n)[ \t]{0,3}#{1,6}[ \t]|(
5294
5294
  var LATEX_COMMAND = /\\[a-zA-Z]/;
5295
5295
  var PROSE_WORD = /[A-Za-z]{3,}/g;
5296
5296
  var PROSE_WORD_LIMIT = 6;
5297
+ function isMathPlaceholder(inner) {
5298
+ return /^[\s.…·⋯_-]*$/.test(inner) && /[.…·⋯]/.test(inner);
5299
+ }
5297
5300
  function opensFlowBlock(text, open) {
5298
5301
  for (let k = open - 1; k >= 0 && text[k] !== "\n"; k--) if (text[k] !== " " && text[k] !== " ") return false;
5299
5302
  for (let k = open + 2; k < text.length && text[k] !== "\n"; k++) if (text[k] !== " " && text[k] !== " " && text[k] !== "\r") return false;
@@ -5312,13 +5315,84 @@ function looksLikeDisplayMath(inner, flow = false) {
5312
5315
  }
5313
5316
  function codeRanges(text) {
5314
5317
  const ranges = [];
5315
- for (const re of [/```[\s\S]*?(?:```|$)/g, /~~~[\s\S]*?(?:~~~|$)/g, /`[^`\n]*`/g]) {
5316
- let m;
5317
- while ((m = re.exec(text)) !== null) {
5318
- ranges.push([m.index, m.index + m[0].length]);
5319
- if (m[0].length === 0) re.lastIndex++;
5318
+ const n = text.length;
5319
+ let fence = null;
5320
+ let para = -1;
5321
+ const spans = (from, to) => {
5322
+ const runs = [];
5323
+ for (let k = from; k < to; ) {
5324
+ if (text.charCodeAt(k) !== 96) {
5325
+ k++;
5326
+ continue;
5327
+ }
5328
+ let len = 1;
5329
+ while (k + len < to && text.charCodeAt(k + len) === 96) len++;
5330
+ runs.push([k, len]);
5331
+ k += len;
5332
+ }
5333
+ const nextSame = new Array(runs.length).fill(-1);
5334
+ const last = /* @__PURE__ */ new Map();
5335
+ for (let r = runs.length - 1; r >= 0; r--) {
5336
+ const len = runs[r][1];
5337
+ nextSame[r] = last.get(len) ?? -1;
5338
+ last.set(len, r);
5339
+ }
5340
+ for (let r = 0; r < runs.length; ) {
5341
+ const j = nextSame[r];
5342
+ if (j === -1) {
5343
+ r++;
5344
+ continue;
5345
+ }
5346
+ const [s0] = runs[r];
5347
+ const [s1, l1] = runs[j];
5348
+ ranges.push([s0, s1 + l1]);
5349
+ r = j + 1;
5320
5350
  }
5321
- }
5351
+ };
5352
+ let pos = 0;
5353
+ while (pos <= n) {
5354
+ const nl = text.indexOf("\n", pos);
5355
+ const lineEnd = nl === -1 ? n : nl;
5356
+ const line = text.slice(pos, lineEnd);
5357
+ let indent = 0;
5358
+ while (indent < line.length && line[indent] === " ") indent++;
5359
+ const body = line.slice(indent);
5360
+ if (fence) {
5361
+ const trimmed = trimFenceLine(line);
5362
+ let closes = false;
5363
+ if (fence.char === "`") {
5364
+ const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
5365
+ if (kind === "open-nested") fence.depth++;
5366
+ else if (kind === "close-nested") fence.depth--;
5367
+ else if (kind === "close-outer") closes = indent <= 3;
5368
+ } else {
5369
+ let t = 0;
5370
+ while (t < trimmed.length && trimmed[t] === "~") t++;
5371
+ closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
5372
+ }
5373
+ if (closes) {
5374
+ ranges.push([fence.start, lineEnd]);
5375
+ fence = null;
5376
+ }
5377
+ } else {
5378
+ const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
5379
+ const info = opener ? opener[2] : "";
5380
+ if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
5381
+ if (para !== -1) spans(para, pos);
5382
+ para = -1;
5383
+ const char = opener[1][0];
5384
+ const parsed = char === "`" ? parseFenceOpener(line) : null;
5385
+ fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
5386
+ } else if (body.trim() === "") {
5387
+ if (para !== -1) spans(para, pos);
5388
+ para = -1;
5389
+ } else if (para === -1) para = pos;
5390
+ }
5391
+ if (nl === -1) break;
5392
+ pos = nl + 1;
5393
+ }
5394
+ if (fence) ranges.push([fence.start, n]);
5395
+ else if (para !== -1) spans(para, n);
5322
5396
  return ranges.sort((a, b) => a[0] - b[0]);
5323
5397
  }
5324
5398
  function pendingDisplayMathOpener(text) {
@@ -5341,6 +5415,10 @@ function pendingDisplayMathOpener(text) {
5341
5415
  const open = tokens[j];
5342
5416
  const close = tokens[j + 1];
5343
5417
  if (close === void 0) break;
5418
+ if (isMathPlaceholder(text.slice(open + 2, close))) {
5419
+ j += 2;
5420
+ continue;
5421
+ }
5344
5422
  if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
5345
5423
  paired.add(open);
5346
5424
  paired.add(close);
@@ -5385,6 +5463,10 @@ function pairDisplayMath(text) {
5385
5463
  const open = tokens[j];
5386
5464
  const close = tokens[j + 1];
5387
5465
  if (close === void 0) break;
5466
+ if (isMathPlaceholder(text.slice(open + 2, close))) {
5467
+ j += 2;
5468
+ continue;
5469
+ }
5388
5470
  if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
5389
5471
  pairs.set(open, close);
5390
5472
  j += 2;
@@ -5722,6 +5804,19 @@ var Tokenizer = class {
5722
5804
  this.mathPairs = pairDisplayMath(text);
5723
5805
  this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
5724
5806
  if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
5807
+ if (streaming && text.includes("$$")) {
5808
+ for (const pad of ["`", "``", "```", "~~~"]) {
5809
+ const alt = pairDisplayMath(text + pad);
5810
+ for (const [open, close] of this.mathPairs) {
5811
+ if (alt.get(open) !== close) this.provisionalFrom = Math.min(this.provisionalFrom, open);
5812
+ }
5813
+ for (const [open, close] of alt) {
5814
+ if (close < text.length && this.mathPairs.get(open) !== close) {
5815
+ this.provisionalFrom = Math.min(this.provisionalFrom, open);
5816
+ }
5817
+ }
5818
+ }
5819
+ }
5725
5820
  let start = 0;
5726
5821
  for (let i = 0; i < text.length; i++) {
5727
5822
  if (text.charCodeAt(i) === 10) {
@@ -5762,13 +5857,17 @@ var Tokenizer = class {
5762
5857
  /**
5763
5858
  * Streaming: an open last line that begins with `{` is undecided — each new
5764
5859
  * byte can balance or unbalance its braces and flip the whole line between
5765
- * prose and a JSON island — so it is provisional until it ends (G5).
5860
+ * prose and a JSON island — and so is a line that is exactly one allowed
5861
+ * HTML tag so far; both are provisional until they end (G5).
5766
5862
  */
5767
5863
  markOpenJsonTail() {
5768
5864
  const last = this.lineStarts.length - 1;
5769
5865
  if (!this.isOpenTail(last)) return;
5770
5866
  const start = this.lineStarts[last];
5771
- if (this.lineText(last).trimStart().startsWith("{")) {
5867
+ const t = this.lineText(last).trim();
5868
+ const oneTag = t.startsWith("<") ? readXmlTag(t, 0) : null;
5869
+ const loneTag = !!oneTag && oneTag.raw === t && ALLOWED_RAW_HTML_TAGS.has(oneTag.tagName.toLowerCase());
5870
+ if (t.startsWith("{") || loneTag) {
5772
5871
  this.provisionalFrom = Math.min(this.provisionalFrom, start);
5773
5872
  }
5774
5873
  }
@@ -5897,7 +5996,8 @@ var Tokenizer = class {
5897
5996
  const fm = this.frontMatter(li, t);
5898
5997
  if (fm) return fm;
5899
5998
  }
5900
- if (indent <= 3 && t.startsWith("::")) {
5999
+ const listNested = indent > 0 && this.inListContinuation(li);
6000
+ if (indent <= 3 && !listNested && t.startsWith("::")) {
5901
6001
  const directive = this.directive(p, li, t);
5902
6002
  if (directive) return directive;
5903
6003
  }
@@ -5905,7 +6005,7 @@ var Tokenizer = class {
5905
6005
  const admonition = this.mkdocsAdmonition(p, li, t);
5906
6006
  if (admonition) return admonition;
5907
6007
  }
5908
- if (indent <= 3 && t.startsWith(">")) {
6008
+ if (indent <= 3 && !listNested && t.startsWith(">")) {
5909
6009
  const callout = this.callout(p, li, t);
5910
6010
  if (callout) return callout;
5911
6011
  }
@@ -5946,6 +6046,17 @@ var Tokenizer = class {
5946
6046
  }
5947
6047
  return null;
5948
6048
  }
6049
+ /** True when line `li` continues a list item (the nearest non-blank line above is a list item or indented text under one). */
6050
+ inListContinuation(li) {
6051
+ for (let k = li - 1; k >= 0; k--) {
6052
+ const line = this.lineText(k);
6053
+ if (!NOT_WHITESPACE.test(line)) continue;
6054
+ if (/^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]/.test(line)) return true;
6055
+ if (/^[ \t]/.test(line)) continue;
6056
+ return false;
6057
+ }
6058
+ return false;
6059
+ }
5949
6060
  island(type, start, end, complete, meta = {}) {
5950
6061
  return { type, start, end, complete, meta };
5951
6062
  }
@@ -6050,10 +6161,16 @@ var Tokenizer = class {
6050
6161
  continue;
6051
6162
  }
6052
6163
  if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
6053
- if (this.isOpenTail(k)) break;
6164
+ if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
6054
6165
  last = k;
6055
6166
  }
6056
- return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
6167
+ const end = this.contentEnds[last];
6168
+ if (this.paragraphMayGrow(end)) return this.heldFootnote(p, open[1]);
6169
+ return this.island("footnote_def", p, end, true, { id: open[1] });
6170
+ }
6171
+ /** A footnote whose continuation is still arriving: open to the end of the text. */
6172
+ heldFootnote(p, id) {
6173
+ return this.island("footnote_def", p, this.n, false, { id });
6057
6174
  }
6058
6175
  /**
6059
6176
  * Backtick fence — the renderer splitter's close rules: JSON-string aware for
@@ -6297,7 +6414,12 @@ var Tokenizer = class {
6297
6414
  return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
6298
6415
  }
6299
6416
  const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
6300
- const typeSeven = atParaStart && t.trimEnd() === tag.raw && !this.isOpenTail(li);
6417
+ const loneTag = atParaStart && t.trimEnd() === tag.raw;
6418
+ if (loneTag && this.isOpenTail(li)) {
6419
+ this.provisionalFrom = Math.min(this.provisionalFrom, p);
6420
+ return null;
6421
+ }
6422
+ const typeSeven = loneTag;
6301
6423
  if (!typeSix && !typeSeven) return null;
6302
6424
  let end = this.contentEnds[li];
6303
6425
  for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
@@ -6389,10 +6511,6 @@ var Tokenizer = class {
6389
6511
  let breakLine = -1;
6390
6512
  for (let k = li + 1; k < this.lineCount; k++) {
6391
6513
  if (this.lineIsBlank(k)) break;
6392
- if (this.lineStarts[k] >= this.provisionalFrom) {
6393
- end = this.n;
6394
- break;
6395
- }
6396
6514
  if (this.detectBlock(this.lineStarts[k], k, false)) {
6397
6515
  breakLine = k;
6398
6516
  break;
@@ -6535,7 +6653,11 @@ var Tokenizer = class {
6535
6653
  }
6536
6654
  const end = singleDollarMathEnd(text, i, pe);
6537
6655
  this.tick(end > 0 ? end - i : Math.min(403, pe - i));
6538
- if (end > 0 && !(this.streaming && end === this.n)) {
6656
+ if (end > 0 && this.streaming && end === this.n) {
6657
+ this.provisionalFrom = Math.min(this.provisionalFrom, i);
6658
+ return { inlines, breakIsland: null, orphan: false };
6659
+ }
6660
+ if (end > 0) {
6539
6661
  inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
6540
6662
  i = end;
6541
6663
  continue;
@@ -7105,6 +7227,7 @@ exports.isIrEnvelopeCache = isIrEnvelopeCache;
7105
7227
  exports.isJsonAnyField = isJsonAnyField;
7106
7228
  exports.isKindDirective = isKindDirective;
7107
7229
  exports.isLegacyShell = isLegacyShell;
7230
+ exports.isMathPlaceholder = isMathPlaceholder;
7108
7231
  exports.isProvisionalKind = isProvisionalKind;
7109
7232
  exports.isReservedDirectiveSlug = isReservedDirectiveSlug;
7110
7233
  exports.isScalarArrayType = isScalarArrayType;