@ai-matrx/content-ir 0.17.0 → 0.18.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/index.js CHANGED
@@ -5221,75 +5221,231 @@ function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
5221
5221
  return "content";
5222
5222
  }
5223
5223
 
5224
- // source/math.ts
5225
- var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
5226
- var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
5227
- var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
5228
- var FUNCTION_NAMES = /* @__PURE__ */ new Set([
5229
- "sin",
5230
- "cos",
5231
- "tan",
5232
- "sec",
5233
- "csc",
5234
- "cot",
5235
- "log",
5236
- "ln",
5237
- "exp",
5238
- "lim",
5239
- "max",
5240
- "min",
5241
- "det",
5242
- "gcd",
5243
- "mod",
5244
- "arg",
5245
- "sinh",
5246
- "cosh",
5247
- "tanh"
5248
- ]);
5249
- function isMathShape(content) {
5250
- if (!MATH_SHAPE_CHARS.test(content)) return false;
5251
- const words = content.match(/[A-Za-z]+/g);
5252
- if (!words) return false;
5253
- if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
5254
- if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
5255
- return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
5224
+ // source/code-ranges.ts
5225
+ function fenceOpenerOf(content) {
5226
+ const t = content.trimStart();
5227
+ if (t.startsWith("```")) {
5228
+ let ticks = 0;
5229
+ while (ticks < t.length && t[ticks] === "`") ticks++;
5230
+ return { char: "`", ticks, lang: parseFenceOpener(t)?.lang ?? "" };
5231
+ }
5232
+ if (content.length - t.length <= 3 && t.startsWith("~~~")) {
5233
+ const open = /^(~{3,})(.*)$/.exec(t);
5234
+ if (!open) return null;
5235
+ return { char: "~", ticks: open[1].length, lang: open[2].trim().split(/\s/)[0] ?? "" };
5236
+ }
5237
+ return null;
5256
5238
  }
5257
- function isSingleDollarMath(content, after) {
5258
- if (!content || content.length > 400) return false;
5259
- if (/^\s/.test(content) || /\s$/.test(content)) return false;
5260
- if (after !== void 0 && /[0-9]/.test(after)) return false;
5261
- if (LONE_VARIABLE.test(content)) return true;
5262
- if (/^[0-9]/.test(content)) return /\\[A-Za-z]+|[\\^_{}]/.test(content);
5263
- return TEX_SIGNAL.test(content) || isMathShape(content);
5239
+ var FenceReader = class {
5240
+ constructor(opener, options = {}) {
5241
+ this.opener = opener;
5242
+ this.isJson = opener.char === "`" && opener.lang === "json";
5243
+ this.nesting = opener.char === "`" && (options.nesting ?? fenceNestsInnerFences(opener.lang));
5244
+ this.tildeCloser = opener.char === "~" ? new RegExp(`^ {0,3}~{${opener.ticks},}[ \\t]*$`) : null;
5245
+ }
5246
+ opener;
5247
+ state = { inString: false, escaped: false };
5248
+ depth = 0;
5249
+ /** A nested fence was opened at some point. */
5250
+ sawNested = false;
5251
+ isJson;
5252
+ nesting;
5253
+ tildeCloser;
5254
+ /** How many nested fences are open right now. */
5255
+ get nestedDepth() {
5256
+ return this.depth;
5257
+ }
5258
+ feed(line) {
5259
+ if (this.tildeCloser) return this.tildeCloser.test(line);
5260
+ const trimmedLine = trimFenceLine(line);
5261
+ const at = line.indexOf("```");
5262
+ if (at === -1) {
5263
+ if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5264
+ return false;
5265
+ }
5266
+ if (this.isJson) {
5267
+ const probe = jsonStringState(line, 0, at, this.state);
5268
+ if (probe.inString) {
5269
+ this.state = jsonStringState(line, at, line.length, probe);
5270
+ return false;
5271
+ }
5272
+ }
5273
+ if (trimmedLine.startsWith("```")) {
5274
+ const kind = classifyInnerFenceLine(trimmedLine, this.opener.ticks, this.nesting, this.depth);
5275
+ if (kind === "close-outer") return true;
5276
+ if (kind === "open-nested") {
5277
+ this.depth++;
5278
+ this.sawNested = true;
5279
+ }
5280
+ if (kind === "close-nested") this.depth--;
5281
+ } else {
5282
+ const closeTicks = backtickRunLength(line, at);
5283
+ const after = trimFenceLine(line.slice(at + closeTicks));
5284
+ if (closeTicks >= this.opener.ticks && after === "" && this.depth === 0) return true;
5285
+ }
5286
+ if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5287
+ return false;
5288
+ }
5289
+ };
5290
+ function closeFence(src, open, opener) {
5291
+ const first = readFence(src, open, new FenceReader(opener));
5292
+ if (first.closed || !first.reader.sawNested || first.reader.nestedDepth <= 0) return { line: first.line, closed: first.closed };
5293
+ const strict = readFence(src, open, new FenceReader(opener, { nesting: false }));
5294
+ return { line: strict.line, closed: strict.closed };
5264
5295
  }
5265
- function singleDollarMathEnd(text, open, limit = text.length) {
5266
- const bound = Math.min(limit, open + 403);
5267
- let close = -1;
5268
- for (let k = open + 1; k < bound; k += 1) {
5269
- const c = text[k];
5270
- if (c === "\n") break;
5271
- if (c === "\\") {
5272
- k += 1;
5296
+ function readFence(src, open, reader) {
5297
+ for (let k = open + 1; k < src.count; k++) {
5298
+ if (src.openTail?.(k)) continue;
5299
+ const line = src.line(k);
5300
+ if (line === null) return { line: k - 1, closed: false, reader };
5301
+ if (reader.feed(line)) return { line: k, closed: true, reader };
5302
+ }
5303
+ return { line: src.count - 1, closed: false, reader };
5304
+ }
5305
+ function jsonStringState(line, from, upTo, state) {
5306
+ let { inString, escaped } = state;
5307
+ for (let idx = from; idx < upTo; idx++) {
5308
+ const ch = line[idx];
5309
+ if (escaped) {
5310
+ escaped = false;
5273
5311
  continue;
5274
5312
  }
5275
- if (c === "$") {
5276
- close = k;
5277
- break;
5313
+ if (ch === "\\" && inString) {
5314
+ escaped = true;
5315
+ continue;
5278
5316
  }
5317
+ if (ch === '"') inString = !inString;
5279
5318
  }
5280
- if (close === -1 || text[close + 1] === "$") return -1;
5281
- const content = text.slice(open + 1, close);
5282
- return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
5319
+ return { inString, escaped };
5283
5320
  }
5284
-
5285
- // source/code-ranges.ts
5286
- function findCodeRanges(text) {
5287
- const ranges = [];
5288
- const n = text.length;
5289
- let fence = null;
5321
+ function backtickRunLength(str2, pos) {
5322
+ let n = 0;
5323
+ while (pos + n < str2.length && str2[pos + n] === "`") n++;
5324
+ return n;
5325
+ }
5326
+ function quotePrefix(line) {
5327
+ let depth = 0;
5328
+ let offset = 0;
5329
+ for (; ; ) {
5330
+ const m = /^ {0,3}> ?/.exec(line.slice(offset));
5331
+ if (!m) return { depth, offset };
5332
+ depth++;
5333
+ offset += m[0].length;
5334
+ }
5335
+ }
5336
+ function stripQuotes(line, depth) {
5337
+ let offset = 0;
5338
+ for (let d = 0; d < depth; d++) {
5339
+ const m = /^ {0,3}> ?/.exec(line.slice(offset));
5340
+ if (!m) return null;
5341
+ offset += m[0].length;
5342
+ }
5343
+ return line.slice(offset);
5344
+ }
5345
+ var LIST_ITEM = /^( *)([-*+]|\d{1,9}[.)])( +|\t|$)/;
5346
+ function leadingSpaces(s) {
5347
+ let i = 0;
5348
+ while (i < s.length && s[i] === " ") i++;
5349
+ return i;
5350
+ }
5351
+ function scan(text) {
5352
+ const starts = [];
5353
+ const ends = [];
5354
+ for (let pos = 0; ; ) {
5355
+ const nl = text.indexOf("\n", pos);
5356
+ const lineEnd = nl === -1 ? text.length : nl;
5357
+ starts.push(pos);
5358
+ ends.push(lineEnd > pos && text.charCodeAt(lineEnd - 1) === 13 ? lineEnd - 1 : lineEnd);
5359
+ if (nl === -1) break;
5360
+ pos = nl + 1;
5361
+ }
5362
+ const count = starts.length;
5363
+ const raw = (k) => text.slice(starts[k], ends[k]);
5364
+ const out = { ranges: [], paragraphs: [], endsInFence: false, pendingPara: -1 };
5365
+ const rawLines = { count, line: raw };
5366
+ let segment = 0;
5367
+ for (let k = 0; k < count; k++) {
5368
+ const opener = fenceOpenerOf(raw(k));
5369
+ if (!opener || opener.char !== "`") continue;
5370
+ prose(text, starts, ends, raw, segment, k, out);
5371
+ const end = closeFence(rawLines, k, opener);
5372
+ out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
5373
+ out.pendingPara = -1;
5374
+ if (!end.closed && end.line === count - 1) out.endsInFence = true;
5375
+ k = end.line;
5376
+ segment = k + 1;
5377
+ }
5378
+ prose(text, starts, ends, raw, segment, count, out);
5379
+ return out;
5380
+ }
5381
+ function prose(text, starts, ends, raw, from, to, out) {
5290
5382
  let para = -1;
5291
- const spans = (from, to) => {
5292
- const runs = [];
5383
+ let quoteDepth = 0;
5384
+ let lists = [];
5385
+ const closePara = (at) => {
5386
+ if (para !== -1) out.paragraphs.push([para, at]);
5387
+ para = -1;
5388
+ };
5389
+ for (let k = from; k < to; k++) {
5390
+ const line = raw(k);
5391
+ const { depth, offset } = quotePrefix(line);
5392
+ if (depth !== quoteDepth) {
5393
+ quoteDepth = depth;
5394
+ lists = [];
5395
+ }
5396
+ let content = line.slice(offset);
5397
+ if (content.trim() === "") {
5398
+ closePara(starts[k]);
5399
+ if (!(k === starts.length - 1 && line === "")) out.pendingPara = -1;
5400
+ continue;
5401
+ }
5402
+ const indent = leadingSpaces(content);
5403
+ while (lists.length && indent < lists[lists.length - 1]) lists.pop();
5404
+ const item = LIST_ITEM.exec(content);
5405
+ let column = lists.length ? lists[lists.length - 1] : 0;
5406
+ if (item) {
5407
+ const gap = item[3].length;
5408
+ column = item[1].length + item[2].length + (gap >= 1 && gap <= 4 ? gap : 1);
5409
+ lists.push(column);
5410
+ content = content.slice(Math.min(column, content.length));
5411
+ } else {
5412
+ content = content.slice(Math.min(indent, column));
5413
+ }
5414
+ const opener = fenceOpenerOf(content);
5415
+ if (!opener) {
5416
+ if (para === -1) para = starts[k];
5417
+ if (out.pendingPara === -1) out.pendingPara = starts[k];
5418
+ continue;
5419
+ }
5420
+ closePara(starts[k]);
5421
+ out.pendingPara = -1;
5422
+ const col = column;
5423
+ const src = {
5424
+ count: to,
5425
+ line: (j) => {
5426
+ const inner = stripQuotes(raw(j), depth);
5427
+ if (inner === null) return null;
5428
+ if (opener.char === "~" && col > 0) {
5429
+ const ind = leadingSpaces(inner);
5430
+ if (inner.trim() !== "" && ind < col) return null;
5431
+ return inner.slice(Math.min(ind, col));
5432
+ }
5433
+ return inner;
5434
+ }
5435
+ };
5436
+ const end = closeFence(src, k, opener);
5437
+ out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
5438
+ if (!end.closed && end.line === starts.length - 1) out.endsInFence = true;
5439
+ k = end.line;
5440
+ }
5441
+ closePara(to < starts.length ? starts[to] : text.length);
5442
+ }
5443
+ var BacktickRuns = class {
5444
+ /** Every run, in order: [start, length]. */
5445
+ all = [];
5446
+ byLength = /* @__PURE__ */ new Map();
5447
+ indexOf = /* @__PURE__ */ new Map();
5448
+ constructor(text, from, to) {
5293
5449
  for (let k = from; k < to; ) {
5294
5450
  if (text.charCodeAt(k) !== 96) {
5295
5451
  k++;
@@ -5297,117 +5453,57 @@ function findCodeRanges(text) {
5297
5453
  }
5298
5454
  let len = 1;
5299
5455
  while (k + len < to && text.charCodeAt(k + len) === 96) len++;
5300
- runs.push([k, len]);
5456
+ this.indexOf.set(k, this.all.length);
5457
+ this.all.push([k, len]);
5458
+ const list = this.byLength.get(len);
5459
+ if (list) list.push(k);
5460
+ else this.byLength.set(len, [k]);
5301
5461
  k += len;
5302
5462
  }
5303
- const nextSame = new Array(runs.length).fill(-1);
5304
- const last = /* @__PURE__ */ new Map();
5305
- for (let r = runs.length - 1; r >= 0; r--) {
5306
- const len = runs[r][1];
5307
- nextSame[r] = last.get(len) ?? -1;
5308
- last.set(len, r);
5309
- }
5310
- for (let r = 0; r < runs.length; ) {
5311
- const j = nextSame[r];
5312
- if (j === -1) {
5313
- r++;
5314
- continue;
5315
- }
5316
- const [s0] = runs[r];
5317
- const [s1, l1] = runs[j];
5318
- ranges.push({ start: s0, end: s1 + l1, kind: "span" });
5319
- r = j + 1;
5463
+ }
5464
+ /** Start of the first run of exactly `n` backticks at or after `after`, or -1. */
5465
+ next(n, after) {
5466
+ const list = this.byLength.get(n);
5467
+ if (!list) return -1;
5468
+ let lo = 0;
5469
+ let hi = list.length;
5470
+ while (lo < hi) {
5471
+ const mid = lo + hi >>> 1;
5472
+ if (list[mid] < after) lo = mid + 1;
5473
+ else hi = mid;
5320
5474
  }
5321
- };
5322
- let pos = 0;
5323
- while (pos <= n) {
5324
- const nl = text.indexOf("\n", pos);
5325
- const lineEnd = nl === -1 ? n : nl;
5326
- const line = text.slice(pos, lineEnd);
5327
- let indent = 0;
5328
- while (indent < line.length && line[indent] === " ") indent++;
5329
- const body = line.slice(indent);
5330
- if (fence) {
5331
- const trimmed = trimFenceLine(line);
5332
- let closes = false;
5333
- if (fence.char === "`") {
5334
- const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
5335
- if (kind === "open-nested") fence.depth++;
5336
- else if (kind === "close-nested") fence.depth--;
5337
- else if (kind === "close-outer") closes = indent <= 3;
5338
- } else {
5339
- let t = 0;
5340
- while (t < trimmed.length && trimmed[t] === "~") t++;
5341
- closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
5342
- }
5343
- if (closes) {
5344
- ranges.push({ start: fence.start, end: lineEnd, kind: "fence" });
5345
- fence = null;
5346
- }
5347
- } else {
5348
- const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
5349
- const info = opener ? opener[2] : "";
5350
- if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
5351
- if (para !== -1) spans(para, pos);
5352
- para = -1;
5353
- const char = opener[1][0];
5354
- const parsed = char === "`" ? parseFenceOpener(line) : null;
5355
- fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
5356
- } else if (body.trim() === "") {
5357
- if (para !== -1) spans(para, pos);
5358
- para = -1;
5359
- } else if (para === -1) para = pos;
5475
+ return list[lo] ?? -1;
5476
+ }
5477
+ /** Index of the run starting at `start` in `all`. */
5478
+ index(start) {
5479
+ return this.indexOf.get(start) ?? -1;
5480
+ }
5481
+ };
5482
+ function codeSpans(text, from, to, out) {
5483
+ const runs = new BacktickRuns(text, from, to);
5484
+ for (let r = 0; r < runs.all.length; ) {
5485
+ const [start, n] = runs.all[r];
5486
+ const close = runs.next(n, start + n);
5487
+ if (close === -1) {
5488
+ r++;
5489
+ continue;
5360
5490
  }
5361
- if (nl === -1) break;
5362
- pos = nl + 1;
5491
+ out.push({ start, end: close + n, kind: "span" });
5492
+ r = runs.index(close) + 1;
5363
5493
  }
5364
- if (fence) ranges.push({ start: fence.start, end: n, kind: "fence" });
5365
- else if (para !== -1) spans(para, n);
5494
+ }
5495
+ function findCodeRanges(text) {
5496
+ const s = scan(text);
5497
+ const ranges = [...s.ranges];
5498
+ for (const [from, to] of s.paragraphs) codeSpans(text, from, to, ranges);
5366
5499
  return ranges.sort((a, b) => a.start - b.start);
5367
5500
  }
5368
5501
  function pendingCodeSpanStart(text) {
5369
5502
  const n = text.length;
5370
5503
  if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
5371
- let fence = null;
5372
- let para = -1;
5373
- let pos = 0;
5374
- while (pos <= n) {
5375
- const nl = text.indexOf("\n", pos);
5376
- const lineEnd = nl === -1 ? n : nl;
5377
- const line = text.slice(pos, lineEnd);
5378
- let indent = 0;
5379
- while (indent < line.length && line[indent] === " ") indent++;
5380
- const body = line.slice(indent);
5381
- if (fence) {
5382
- const trimmed = trimFenceLine(line);
5383
- let closes = false;
5384
- if (fence.char === "`") {
5385
- const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
5386
- if (kind === "open-nested") fence.depth++;
5387
- else if (kind === "close-nested") fence.depth--;
5388
- else if (kind === "close-outer") closes = indent <= 3;
5389
- } else {
5390
- let t = 0;
5391
- while (t < trimmed.length && trimmed[t] === "~") t++;
5392
- closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
5393
- }
5394
- if (closes) fence = null;
5395
- } else {
5396
- const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
5397
- const info = opener ? opener[2] : "";
5398
- if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
5399
- para = -1;
5400
- const char = opener[1][0];
5401
- const parsed = char === "`" ? parseFenceOpener(line) : null;
5402
- fence = { char, len: opener[1].length, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
5403
- } else if (body.trim() === "") {
5404
- if (!(nl === -1 && line === "")) para = -1;
5405
- } else if (para === -1) para = pos;
5406
- }
5407
- if (nl === -1) break;
5408
- pos = nl + 1;
5409
- }
5410
- if (fence || para === -1) return -1;
5504
+ const s = scan(text);
5505
+ const para = s.pendingPara;
5506
+ if (s.endsInFence || para === -1) return -1;
5411
5507
  const runs = [];
5412
5508
  for (let k = para; k < n; ) {
5413
5509
  if (text.charCodeAt(k) !== 96) {
@@ -5544,6 +5640,83 @@ function pairDisplayMath(text) {
5544
5640
  return pairs;
5545
5641
  }
5546
5642
 
5643
+ // source/math.ts
5644
+ var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
5645
+ var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
5646
+ var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
5647
+ function isSingleDollarMath(content, after) {
5648
+ if (!content || content.length > 400) return false;
5649
+ if (/^\s/.test(content) || /\s$/.test(content)) return false;
5650
+ if (after !== void 0 && /[0-9]/.test(after)) return false;
5651
+ if (/(?:^|[^\\])%/.test(content)) return false;
5652
+ if (/^[)\]}"/]/.test(content) || /[(\[{":;,/]$/.test(content)) return false;
5653
+ if (/^[0-9]/.test(content) && after !== void 0 && /[A-Za-z]/.test(after)) return false;
5654
+ if (isMathPlaceholder(content)) return false;
5655
+ return TEX_SIGNAL.test(content) || !readsAsProse(content);
5656
+ }
5657
+ var FUNCTION_NAMES = /* @__PURE__ */ new Set([
5658
+ "sin",
5659
+ "cos",
5660
+ "tan",
5661
+ "sec",
5662
+ "csc",
5663
+ "cot",
5664
+ "log",
5665
+ "ln",
5666
+ "exp",
5667
+ "lim",
5668
+ "max",
5669
+ "min",
5670
+ "det",
5671
+ "gcd",
5672
+ "mod",
5673
+ "arg",
5674
+ "sinh",
5675
+ "cosh",
5676
+ "tanh"
5677
+ ]);
5678
+ function readsAsProse(content) {
5679
+ if (/[A-Za-z]'[a-z]/.test(content)) return true;
5680
+ if (/(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return true;
5681
+ if (!/\s/.test(content)) return false;
5682
+ const words = (content.match(/[A-Za-z]{3,}/g) ?? []).filter((w) => !FUNCTION_NAMES.has(w.toLowerCase()));
5683
+ return words.length >= 2;
5684
+ }
5685
+ function looksLikeOpenInlineMath(partial) {
5686
+ if (!partial || partial.length > 400) return false;
5687
+ if (/^\s/.test(partial) || /\s$/.test(partial)) return false;
5688
+ if (LONE_VARIABLE.test(partial)) return true;
5689
+ if (/^[0-9]/.test(partial)) return /\\[A-Za-z]+|[\\^_{}]/.test(partial);
5690
+ return TEX_SIGNAL.test(partial) || isMathShape(partial);
5691
+ }
5692
+ function isMathShape(content) {
5693
+ if (!MATH_SHAPE_CHARS.test(content)) return false;
5694
+ const words = content.match(/[A-Za-z]+/g);
5695
+ if (!words) return false;
5696
+ if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
5697
+ if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
5698
+ return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
5699
+ }
5700
+ function singleDollarMathEnd(text, open, limit = text.length) {
5701
+ const bound = Math.min(limit, open + 403);
5702
+ let close = -1;
5703
+ for (let k = open + 1; k < bound; k += 1) {
5704
+ const c = text[k];
5705
+ if (c === "\n") break;
5706
+ if (c === "\\") {
5707
+ k += 1;
5708
+ continue;
5709
+ }
5710
+ if (c === "$") {
5711
+ close = k;
5712
+ break;
5713
+ }
5714
+ }
5715
+ if (close === -1 || text[close + 1] === "$") return -1;
5716
+ const content = text.slice(open + 1, close);
5717
+ return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
5718
+ }
5719
+
5547
5720
  // source/xml-tag.ts
5548
5721
  var XML_NAME_START = /[A-Za-z_]/;
5549
5722
  var XML_NAME_CHARACTER = /[\w.:-]/;
@@ -5836,27 +6009,6 @@ function isUnclosedGenericXmlOpening(source) {
5836
6009
  }
5837
6010
  return true;
5838
6011
  }
5839
- function jsonStringState(line, from, upTo, state) {
5840
- let { inString, escaped } = state;
5841
- for (let idx = from; idx < upTo; idx++) {
5842
- const ch = line[idx];
5843
- if (escaped) {
5844
- escaped = false;
5845
- continue;
5846
- }
5847
- if (ch === "\\" && inString) {
5848
- escaped = true;
5849
- continue;
5850
- }
5851
- if (ch === '"') inString = !inString;
5852
- }
5853
- return { inString, escaped };
5854
- }
5855
- function backtickRunLength(str2, pos) {
5856
- let n = 0;
5857
- while (pos + n < str2.length && str2[pos + n] === "`") n++;
5858
- return n;
5859
- }
5860
6012
  function isSplitterTreeLine(line) {
5861
6013
  return TREE_GLYPHS.test(line) || /^[\s│|]*[├└+|][\s─\-]+/.test(line);
5862
6014
  }
@@ -6088,11 +6240,9 @@ var Tokenizer = class {
6088
6240
  const footnote = this.footnoteDefinition(p, li, t);
6089
6241
  if (footnote) return footnote;
6090
6242
  }
6091
- if (t.startsWith("```")) return this.backtickFence(p, li, t);
6092
- if (indent <= 3 && t.startsWith("~~~")) {
6093
- const tilde = this.tildeFence(p, li, t);
6094
- if (tilde) return tilde;
6095
- }
6243
+ const fence = fenceOpenerOf(seg);
6244
+ if (fence?.char === "`") return this.backtickFence(p, li, fence);
6245
+ if (fence?.char === "~") return this.tildeFence(p, li, fence);
6096
6246
  if (t.startsWith("$$")) {
6097
6247
  const math = this.mathBlock(p, tp);
6098
6248
  if (math) return math;
@@ -6247,74 +6397,31 @@ var Tokenizer = class {
6247
6397
  heldFootnote(p, id) {
6248
6398
  return this.island("footnote_def", p, this.n, false, { id });
6249
6399
  }
6400
+ /** The text's lines as the one fence closer reads them (streaming: the open tail closes nothing). */
6401
+ get fenceLines() {
6402
+ return {
6403
+ count: this.lineCount,
6404
+ line: (k) => this.lineText(k),
6405
+ openTail: (k) => this.isOpenTail(k)
6406
+ };
6407
+ }
6250
6408
  /**
6251
- * Backtick fence — the renderer splitter's close rules: JSON-string aware for
6252
- * ```json, and THE nested-fence rule (source/fence-nesting.ts) for markdown
6253
- * fences, with the splitter's strict-CommonMark retry when a nested fence is
6254
- * still open at the end of the text.
6409
+ * Backtick fence — THE one closer (`closeFence`, source/code-ranges.ts): the
6410
+ * nested-fence rule for markdown fences with the strict-CommonMark retry, the
6411
+ * renderer's line-end closer, JSON-string awareness for ```json.
6255
6412
  */
6256
- backtickFence(p, li, t) {
6257
- const openTicks = backtickRunLength(t, 0);
6258
- const lang = parseFenceOpener(t)?.lang ?? "";
6413
+ backtickFence(p, li, opener) {
6414
+ const lang = opener.lang;
6259
6415
  const isJson = lang === "json";
6260
6416
  const normalized = normalizeFenceLanguage(lang);
6261
- const meta = { fence: "`", ticks: openTicks, lang };
6417
+ const meta = { fence: "`", ticks: opener.ticks, lang };
6262
6418
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
6263
- const nesting = fenceNestsInnerFences(lang);
6264
- let close = this.backtickFenceClose(li, openTicks, isJson, nesting);
6265
- if (close === "retry-strict") close = this.backtickFenceClose(li, openTicks, isJson, false);
6266
- if (typeof close === "number") {
6267
- return this.fenceIsland(p, this.contentEnds[close], true, meta, li, close, isJson);
6419
+ const end = closeFence(this.fenceLines, li, opener);
6420
+ if (end.closed) {
6421
+ return this.fenceIsland(p, this.contentEnds[end.line], true, meta, li, end.line, isJson);
6268
6422
  }
6269
6423
  return this.fenceIsland(p, this.n, false, meta, li, this.lineCount, isJson);
6270
6424
  }
6271
- /** The closing line of a backtick fence opened on line `li`, or null when it never closes. */
6272
- backtickFenceClose(li, openTicks, isJson, nesting) {
6273
- let state = { inString: false, escaped: false };
6274
- let nestedDepth = 0;
6275
- let sawNested = false;
6276
- for (let k = li + 1; k < this.lineCount; k++) {
6277
- if (this.isOpenTail(k)) continue;
6278
- const line = this.lineText(k);
6279
- const trimmedLine = trimFenceLine(line);
6280
- if (trimmedLine.startsWith("```")) {
6281
- const at2 = line.indexOf("```");
6282
- if (isJson) {
6283
- const probe = jsonStringState(line, 0, at2, state);
6284
- if (probe.inString) {
6285
- state = jsonStringState(line, at2, line.length, probe);
6286
- continue;
6287
- }
6288
- }
6289
- const kind = classifyInnerFenceLine(trimmedLine, openTicks, nesting, nestedDepth);
6290
- if (kind === "close-outer") return k;
6291
- if (kind === "open-nested") {
6292
- nestedDepth++;
6293
- sawNested = true;
6294
- }
6295
- if (kind === "close-nested") nestedDepth--;
6296
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6297
- continue;
6298
- }
6299
- const at = line.indexOf("```");
6300
- if (at !== -1) {
6301
- if (isJson) {
6302
- const probe = jsonStringState(line, 0, at, state);
6303
- if (probe.inString) {
6304
- state = jsonStringState(line, at, line.length, probe);
6305
- continue;
6306
- }
6307
- }
6308
- const closeTicks = backtickRunLength(line, at);
6309
- const after = trimFenceLine(line.slice(at + closeTicks));
6310
- if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
6311
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6312
- continue;
6313
- }
6314
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6315
- }
6316
- return sawNested && nestedDepth > 0 ? "retry-strict" : null;
6317
- }
6318
6425
  fenceIsland(p, end, complete, meta, openLine, closeLine, isJson) {
6319
6426
  if (isJson && openLine + 1 < this.lineCount) {
6320
6427
  const bodyEnd = closeLine < this.lineCount ? this.lineStarts[closeLine] : this.n;
@@ -6323,22 +6430,16 @@ var Tokenizer = class {
6323
6430
  }
6324
6431
  return this.island("fence", p, end, complete, meta);
6325
6432
  }
6326
- tildeFence(p, li, t) {
6327
- const open = /^(~{3,})(.*)$/.exec(t);
6328
- if (!open) return null;
6329
- const ticks = open[1].length;
6330
- const lang = open[2].trim().split(/\s/)[0] ?? "";
6331
- const meta = { fence: "~", ticks, lang };
6433
+ tildeFence(p, li, opener) {
6434
+ const lang = opener.lang;
6435
+ const meta = { fence: "~", ticks: opener.ticks, lang };
6332
6436
  const normalized = normalizeFenceLanguage(lang);
6333
6437
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
6334
- const close = new RegExp(`^ {0,3}~{${ticks},}[ \\t]*$`);
6335
- for (let k = li + 1; k < this.lineCount; k++) {
6336
- if (this.isOpenTail(k)) continue;
6337
- if (close.test(this.lineText(k))) {
6338
- const tildeEnd = this.contentEnds[k];
6339
- const end = this.rendererOverflow(li + 1, k, tildeEnd);
6340
- return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
6341
- }
6438
+ const close = closeFence(this.fenceLines, li, opener);
6439
+ if (close.closed) {
6440
+ const tildeEnd = this.contentEnds[close.line];
6441
+ const end = this.rendererOverflow(li + 1, close.line, tildeEnd);
6442
+ return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
6342
6443
  }
6343
6444
  return this.island("fence", p, this.n, false, meta);
6344
6445
  }
@@ -6358,7 +6459,7 @@ var Tokenizer = class {
6358
6459
  for (let j = firstLine; j <= closeLine && j < this.lineCount; j++) {
6359
6460
  const start = this.lineStarts[j];
6360
6461
  if (this.lineIsBlank(j)) continue;
6361
- if (this.lineText(j).trimStart().startsWith("~~~")) continue;
6462
+ if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
6362
6463
  const inner = this.detectBlockUncached(start, j, false);
6363
6464
  if (!inner) continue;
6364
6465
  if (inner.end > end) {
@@ -6606,9 +6707,9 @@ var Tokenizer = class {
6606
6707
  const tree = { ...breakIsland, start: treeStart };
6607
6708
  if (treeStart > pos) {
6608
6709
  const proseEnd = this.contentEnds[root - 1];
6609
- const scan2 = this.scanInline(pos, proseEnd);
6610
- if (!scan2.breakIsland) {
6611
- this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines: scan2.inlines });
6710
+ const scan3 = this.scanInline(pos, proseEnd);
6711
+ if (!scan3.breakIsland) {
6712
+ this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines: scan3.inlines });
6612
6713
  this.push({ kind: "gap", start: proseEnd, end: treeStart, island: null, inlines: [] });
6613
6714
  this.push({ kind: "island", start: treeStart, end: tree.end, island: tree, inlines: [] });
6614
6715
  return tree.end;
@@ -6620,13 +6721,13 @@ var Tokenizer = class {
6620
6721
  }
6621
6722
  }
6622
6723
  }
6623
- const scan = this.scanInline(pos, end);
6624
- if (!scan.breakIsland) {
6625
- this.push({ kind: "prose", start: pos, end, island: null, inlines: scan.inlines });
6724
+ const scan2 = this.scanInline(pos, end);
6725
+ if (!scan2.breakIsland) {
6726
+ this.push({ kind: "prose", start: pos, end, island: null, inlines: scan2.inlines });
6626
6727
  return end;
6627
6728
  }
6628
- const br = scan.breakIsland;
6629
- if (scan.orphan) {
6729
+ const br = scan2.breakIsland;
6730
+ if (scan2.orphan) {
6630
6731
  let start = pos;
6631
6732
  while (this.blocks.length) {
6632
6733
  const last = this.blocks[this.blocks.length - 1];
@@ -6643,7 +6744,7 @@ var Tokenizer = class {
6643
6744
  start: pos,
6644
6745
  end: br.start,
6645
6746
  island: null,
6646
- inlines: scan.inlines.filter((inline) => inline.end <= br.start)
6747
+ inlines: scan2.inlines.filter((inline) => inline.end <= br.start)
6647
6748
  });
6648
6749
  this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
6649
6750
  return br.end;
@@ -6663,30 +6764,10 @@ var Tokenizer = class {
6663
6764
  const codeSpanClose = (n, from) => {
6664
6765
  if (!runs) {
6665
6766
  this.tick(pe - ps);
6666
- runs = /* @__PURE__ */ new Map();
6667
- for (let k = ps; k < pe; ) {
6668
- if (text.charCodeAt(k) !== 96) {
6669
- k++;
6670
- continue;
6671
- }
6672
- const len = backtickRunLength(text, k);
6673
- const list2 = runs.get(len);
6674
- if (list2) list2.push(k);
6675
- else runs.set(len, [k]);
6676
- k += len;
6677
- }
6767
+ runs = new BacktickRuns(text, ps, pe);
6678
6768
  }
6679
- const list = runs.get(n);
6680
- if (!list) return -1;
6681
- let lo = 0;
6682
- let hi = list.length;
6683
- while (lo < hi) {
6684
- const mid = lo + hi >>> 1;
6685
- if (list[mid] < from) lo = mid + 1;
6686
- else hi = mid;
6687
- }
6688
- const at = list[lo];
6689
- return at !== void 0 && at + n <= pe ? at : -1;
6769
+ const at = runs.next(n, from);
6770
+ return at !== -1 && at + n <= pe ? at : -1;
6690
6771
  };
6691
6772
  let i = ps;
6692
6773
  const scanEnd = Math.min(pe, this.provisionalFrom);
@@ -7223,6 +7304,6 @@ function mapRange(changes, start, end, unit = "utf16") {
7223
7304
  return { start: newStart, end: newEnd, touched, collapsed: newEnd === newStart && end > start };
7224
7305
  }
7225
7306
 
7226
- export { ACTION_BY_CLASS, CALLOUT_NAMES, CAPABILITY_BY_CLASS, CLASSES, DIRECTIVE_VERSION, DirectiveDecodeError, FENCE_OPENER_WHITESPACE, FENCE_WHITESPACE, IN_CONTENT_CLASSES, IR_ENVELOPE_CACHE_VERSION, IR_ENVELOPE_KEY, IR_PARTIAL_KEY, IR_VERSION, IrTree, JSON_DISCRIMINATOR, JsonStreamTokenizer, KIND_KEY, KindStorageError, KindStreamParser, NESTING_FENCE_LANGUAGES, NODE_OUTCOME_KIND, ParseSession, RESERVED_PREFIX, ROOT_STORAGE_NAME, RUN_RESULT_KIND, SIDE_EFFECT_CLASSES, SLUG_PREFIX, SourceSpliceError, XmlContainerTracker, advancePartialKind, asKindInstance, blockAt, blockEdit, blockRangeEdit, buildAgentSchemaWithRenderBlockSupport, buildCodePointIndex, buildCompliantKindSnapshot, buildDirectiveSlug, buildKindDirective, capabilityOf, classifyInboundEnvelopeMetadata, classifyInnerFenceLine, collectReferencedKinds, collectSchemaReferencedKinds, compareWithExistingKindSchema, convertAiSchemaToBlockFields, createFingerprinter, createKindStreamParser, decodeDirective, describeDualGateFailure, directiveDisplay, directiveItemKindFromEdges, directiveSlugOf, disposeParseSession, emitPayloadFence, emitPayloadJson, emptyValueForFieldSchema, envelopeCacheFromEnvelopes, envelopeFromCompleteValue, executesAtOutputRoot, fenceDiscriminator, fenceNestsInnerFences, fieldsToDbPayload, findCodeRanges, fingerprintText, formatBlockLabel, getParseSession, injectKindIntoObjectSchema, irPathIsUnderOrEqual, irPathKey, irPathLabel, irPathsEqual, isCanonicalBlockIR, isDirectiveClass, isDuplicateBlockSlug, isEmptyResidue, isIrEnvelopeCache, isJsonAnyField, isKindDirective, isLegacyShell, isMathPlaceholder, isProvisionalKind, isReservedDirectiveSlug, isScalarArrayType, isSingleDollarMath, isTerminalKindEvent, islandEdit, itemFacts, itemSubtitle, itemTitle, joinSource, kindSchemaFromJsonSchema, kindSchemaToJsonSchema, kindSchemaToStorage, kindVerdictOf, legacyShellUses, listIslands, looksLikeDirectiveHead, looksLikeDisplayMath, makePartialKindStalenessGate, mapPosition, mapRange, mergeResidueIntoValue, normalizeAiSchemaInput, normalizeJsonRegion, nounFamily, nounLabel, nounTitleColumn, openParseSession, pairDisplayMath, parseDirectiveSlug, parseFenceOpener, pendingDisplayMathOpener, readEnvelope, readNodeOutcomeValue, readObjectKind, readOutputRef, readPartialKindEvent, readRunResultValue, readXmlTag, reconstructRegionValue, rehydrateNodeOutcome, rehydrateRunResult, resetLegacyShellUses, resolvesInContent, reuseEnvelopeIfCurrent, runKindDualGate, runSchemaConversion, sanitizeInboundEnvelopeMetadata, sanitizeInboundPartialKindMetadata, scalarArrayItemType, schemaLayoutMode, schemaStructureDepth, setJsonRootKeyLookup, singleDollarMathEnd, spliceSave, splitsSurrogatePair, storageToKindSchema, stripKindDeep, titleCaseToken, toCodePointOffset, toUtf16Offset, tokenizeSource, trimFenceLine, tryDecodeDirective, tryDecodeDirectiveContent, validateBlockSchemaSavePlan, validateStructuralLeg, withRootKind, xmlDiscriminator };
7307
+ export { ACTION_BY_CLASS, CALLOUT_NAMES, CAPABILITY_BY_CLASS, CLASSES, DIRECTIVE_VERSION, DirectiveDecodeError, FENCE_OPENER_WHITESPACE, FENCE_WHITESPACE, IN_CONTENT_CLASSES, IR_ENVELOPE_CACHE_VERSION, IR_ENVELOPE_KEY, IR_PARTIAL_KEY, IR_VERSION, IrTree, JSON_DISCRIMINATOR, JsonStreamTokenizer, KIND_KEY, KindStorageError, KindStreamParser, NESTING_FENCE_LANGUAGES, NODE_OUTCOME_KIND, ParseSession, RESERVED_PREFIX, ROOT_STORAGE_NAME, RUN_RESULT_KIND, SIDE_EFFECT_CLASSES, SLUG_PREFIX, SourceSpliceError, XmlContainerTracker, advancePartialKind, asKindInstance, blockAt, blockEdit, blockRangeEdit, buildAgentSchemaWithRenderBlockSupport, buildCodePointIndex, buildCompliantKindSnapshot, buildDirectiveSlug, buildKindDirective, capabilityOf, classifyInboundEnvelopeMetadata, classifyInnerFenceLine, collectReferencedKinds, collectSchemaReferencedKinds, compareWithExistingKindSchema, convertAiSchemaToBlockFields, createFingerprinter, createKindStreamParser, decodeDirective, describeDualGateFailure, directiveDisplay, directiveItemKindFromEdges, directiveSlugOf, disposeParseSession, emitPayloadFence, emitPayloadJson, emptyValueForFieldSchema, envelopeCacheFromEnvelopes, envelopeFromCompleteValue, executesAtOutputRoot, fenceDiscriminator, fenceNestsInnerFences, fieldsToDbPayload, findCodeRanges, fingerprintText, formatBlockLabel, getParseSession, injectKindIntoObjectSchema, irPathIsUnderOrEqual, irPathKey, irPathLabel, irPathsEqual, isCanonicalBlockIR, isDirectiveClass, isDuplicateBlockSlug, isEmptyResidue, isIrEnvelopeCache, isJsonAnyField, isKindDirective, isLegacyShell, isMathPlaceholder, isProvisionalKind, isReservedDirectiveSlug, isScalarArrayType, isSingleDollarMath, isTerminalKindEvent, islandEdit, itemFacts, itemSubtitle, itemTitle, joinSource, kindSchemaFromJsonSchema, kindSchemaToJsonSchema, kindSchemaToStorage, kindVerdictOf, legacyShellUses, listIslands, looksLikeDirectiveHead, looksLikeDisplayMath, looksLikeOpenInlineMath, makePartialKindStalenessGate, mapPosition, mapRange, mergeResidueIntoValue, normalizeAiSchemaInput, normalizeJsonRegion, nounFamily, nounLabel, nounTitleColumn, openParseSession, pairDisplayMath, parseDirectiveSlug, parseFenceOpener, pendingDisplayMathOpener, readEnvelope, readNodeOutcomeValue, readObjectKind, readOutputRef, readPartialKindEvent, readRunResultValue, readXmlTag, reconstructRegionValue, rehydrateNodeOutcome, rehydrateRunResult, resetLegacyShellUses, resolvesInContent, reuseEnvelopeIfCurrent, runKindDualGate, runSchemaConversion, sanitizeInboundEnvelopeMetadata, sanitizeInboundPartialKindMetadata, scalarArrayItemType, schemaLayoutMode, schemaStructureDepth, setJsonRootKeyLookup, singleDollarMathEnd, spliceSave, splitsSurrogatePair, storageToKindSchema, stripKindDeep, titleCaseToken, toCodePointOffset, toUtf16Offset, tokenizeSource, trimFenceLine, tryDecodeDirective, tryDecodeDirectiveContent, validateBlockSchemaSavePlan, validateStructuralLeg, withRootKind, xmlDiscriminator };
7227
7308
  //# sourceMappingURL=index.js.map
7228
7309
  //# sourceMappingURL=index.js.map