@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/CHANGELOG.md +63 -0
- package/README.md +24 -0
- package/dist/index.cjs +212 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +211 -20
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +212 -19
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +31 -15
- package/dist/source.d.ts +31 -15
- package/dist/source.js +211 -20
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.0 — 2026-09-26
|
|
4
|
+
|
|
5
|
+
**`/source` streaming: what "settled" promises, pinned — and the one
|
|
6
|
+
documented exception** (verify-RC-B3 § Re-verification (0.16.0): G3, G6, H1,
|
|
7
|
+
H2). The verifier's mutations M1–M13 each turn
|
|
8
|
+
`__tests__/source-attack.test.ts` red ON ITS OWN — including the three
|
|
9
|
+
holdbacks the 0.16.0 suite could not see (M9 unclosed backtick, M10 open `{`
|
|
10
|
+
line, M12 pending `$$`), the `$$` open-tail rule (M11) and the held footnote
|
|
11
|
+
(M13). Unmutated: green in CI and in 10 consecutive long runs under 24 CPU
|
|
12
|
+
spinners.
|
|
13
|
+
|
|
14
|
+
- **G3** — the suite's bounds are counts, never the clock, so no test may be
|
|
15
|
+
failed by the clock either: an explicit suite-wide timeout (15 min CI, 1 h
|
|
16
|
+
long). 0.16.0's long mode died on vitest's default 5 s timeout while every
|
|
17
|
+
work assertion held.
|
|
18
|
+
- **G6** — each streaming holdback is pinned directly and by a flicker
|
|
19
|
+
message, and a new property checks, over random documents with and without
|
|
20
|
+
a final newline, that every island a stream shows as settled is the one-shot
|
|
21
|
+
island.
|
|
22
|
+
- **H1** — a footnote whose continuation is undecided (it ends the text, or
|
|
23
|
+
the next line is still arriving — even one that currently looks like a
|
|
24
|
+
block) is held open (`complete: false`), never shown settled.
|
|
25
|
+
- **H2 (chair ruling, 2026-09-26)** — a late orphan `</thinking>` /
|
|
26
|
+
`</think>` / `</reasoning>` closer is the ONE documented exception: it folds
|
|
27
|
+
the text before it into a thinking region retroactively. Consumers re-read
|
|
28
|
+
the finished text one-shot when a stream completes (README § streaming
|
|
29
|
+
mode); matrx-frontend's chat renderer does the same for its accumulator.
|
|
30
|
+
- The property found and fixed further settle-then-change classes, each now
|
|
31
|
+
held back in streaming mode: a lone allowed HTML tag at any block start on
|
|
32
|
+
the open line; an undecided single-`$` span (everything from its opener);
|
|
33
|
+
a code span the final paragraph may still close (`pendingCodeSpanStart`,
|
|
34
|
+
counting a single trailing newline as still-open) — held only for the `$$`
|
|
35
|
+
readings it can change; `$$` pairs that a completing backtick / tilde run
|
|
36
|
+
would re-pair. The paragraph's end is always computed exactly as one-shot
|
|
37
|
+
computes it; only what is reported stops at the provisional point.
|
|
38
|
+
|
|
39
|
+
**New (`/source` `findCodeRanges`, `CodeRange`): THE ONE code-range rule**
|
|
40
|
+
(RC-B10 one-canonical). Fenced blocks and inline code spans by CommonMark —
|
|
41
|
+
a fence opens only at line start (≤3 spaces) and closes by the one
|
|
42
|
+
fence-nesting rule; a code span pairs equal backtick runs within one
|
|
43
|
+
paragraph. Moved out of `math-pairs` (with `pendingCodeSpanStart`) so every
|
|
44
|
+
package asks one function: `@ai-matrx/kit` delimiter-guard and
|
|
45
|
+
`@ai-matrx/chat` speakable text now import it. Shared vectors:
|
|
46
|
+
`__tests__/fixtures/code-range-vectors.json` (read by content-ir and kit).
|
|
47
|
+
Guard: `__tests__/one-code-range-rule.test.ts` fails on any NEW fence/code-span
|
|
48
|
+
detection in `apps/shared` (shrink-only census of the existing walkers).
|
|
49
|
+
|
|
50
|
+
**Consumer action:** streaming consumers re-tokenize one-shot on completion
|
|
51
|
+
(the H2 contract). Stored text is unaffected.
|
|
52
|
+
|
|
53
|
+
## 0.16.2
|
|
54
|
+
|
|
55
|
+
- **Fixed (`/source` `pairDisplayMath`, `pendingDisplayMathOpener`):** a
|
|
56
|
+
`$$...$$` / `$$…$$` placeholder in prose was paired as display math. New
|
|
57
|
+
`isMathPlaceholder(inner)`; such a pair is consumed whole as literal text.
|
|
58
|
+
Same rule as `@ai-matrx/kit` `delimiter-guard`.
|
|
59
|
+
- **Fixed (`/source` `pairDisplayMath`): an inline triple backtick in prose
|
|
60
|
+
hid every later `$$` formula.** Code is now found the CommonMark way — a fence
|
|
61
|
+
opens only at line start (≤3 spaces), closes by the one fence-nesting rule
|
|
62
|
+
(`classifyInnerFenceLine`), and a code span pairs equal backtick runs inside
|
|
63
|
+
one paragraph. `tokenizeSource` math islands inherit it.
|
|
64
|
+
- Consumer action: none — refresh to latest.
|
|
65
|
+
|
|
3
66
|
## 0.16.1
|
|
4
67
|
|
|
5
68
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
package/README.md
CHANGED
|
@@ -43,6 +43,30 @@ import {
|
|
|
43
43
|
|
|
44
44
|
The package has no browser, React runtime, application store, database client, routing, or renderer dependency. `ajv` is its only runtime dependency. Host-specific registry fetching, component compilation, rendering, and error capture remain adapters around this kernel.
|
|
45
45
|
|
|
46
|
+
## `/source` streaming mode: what "settled" promises
|
|
47
|
+
|
|
48
|
+
`tokenizeSource(text)` reads stored text; `tokenizeSource(prefix, { streaming: true })`
|
|
49
|
+
reads a message still arriving. In streaming mode anything the next bytes could
|
|
50
|
+
still change is held back as plain prose (an open last line, an unclosed code
|
|
51
|
+
span, a `$$` that can still pair, a footnote whose continuation is undecided, …),
|
|
52
|
+
so **an island reported `complete` is the island the finished text has** — with
|
|
53
|
+
ONE documented exception:
|
|
54
|
+
|
|
55
|
+
- **A late orphan reasoning closer.** Models do emit closer-only reasoning (the
|
|
56
|
+
`<thinking>` opener lost upstream). When a `</thinking>` / `</think>` /
|
|
57
|
+
`</reasoning>` arrives with no opener, the one-shot reading folds everything
|
|
58
|
+
before it into a thinking region — retroactively. No streaming reader can
|
|
59
|
+
know that in advance short of holding ALL prose provisional until the stream
|
|
60
|
+
ends, which costs more than it saves (chair ruling, 2026-09-26; common-docs
|
|
61
|
+
`projects/rich-content-unification/REGISTER.md`, RC-B3 rulings).
|
|
62
|
+
|
|
63
|
+
**Consumer contract:** when a stream completes, re-read the finished text in
|
|
64
|
+
one-shot mode (`tokenizeSource(text)`, no `streaming`) and show THAT — the
|
|
65
|
+
final screen always equals the one-shot result, which is also what a reload
|
|
66
|
+
shows. Stored text never goes through streaming mode, so none of this can
|
|
67
|
+
change a stored byte. (matrx-frontend's chat renderer does the same for its
|
|
68
|
+
own live accumulator: `components/mardown-display/chat-markdown/settle-stream-blocks.ts`.)
|
|
69
|
+
|
|
46
70
|
## Release gate
|
|
47
71
|
|
|
48
72
|
```bash
|
package/dist/index.cjs
CHANGED
|
@@ -5288,12 +5288,167 @@ function singleDollarMathEnd(text, open, limit = text.length) {
|
|
|
5288
5288
|
return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
|
|
5289
5289
|
}
|
|
5290
5290
|
|
|
5291
|
+
// source/code-ranges.ts
|
|
5292
|
+
function findCodeRanges(text) {
|
|
5293
|
+
const ranges = [];
|
|
5294
|
+
const n = text.length;
|
|
5295
|
+
let fence = null;
|
|
5296
|
+
let para = -1;
|
|
5297
|
+
const spans = (from, to) => {
|
|
5298
|
+
const runs = [];
|
|
5299
|
+
for (let k = from; k < to; ) {
|
|
5300
|
+
if (text.charCodeAt(k) !== 96) {
|
|
5301
|
+
k++;
|
|
5302
|
+
continue;
|
|
5303
|
+
}
|
|
5304
|
+
let len = 1;
|
|
5305
|
+
while (k + len < to && text.charCodeAt(k + len) === 96) len++;
|
|
5306
|
+
runs.push([k, len]);
|
|
5307
|
+
k += len;
|
|
5308
|
+
}
|
|
5309
|
+
const nextSame = new Array(runs.length).fill(-1);
|
|
5310
|
+
const last = /* @__PURE__ */ new Map();
|
|
5311
|
+
for (let r = runs.length - 1; r >= 0; r--) {
|
|
5312
|
+
const len = runs[r][1];
|
|
5313
|
+
nextSame[r] = last.get(len) ?? -1;
|
|
5314
|
+
last.set(len, r);
|
|
5315
|
+
}
|
|
5316
|
+
for (let r = 0; r < runs.length; ) {
|
|
5317
|
+
const j = nextSame[r];
|
|
5318
|
+
if (j === -1) {
|
|
5319
|
+
r++;
|
|
5320
|
+
continue;
|
|
5321
|
+
}
|
|
5322
|
+
const [s0] = runs[r];
|
|
5323
|
+
const [s1, l1] = runs[j];
|
|
5324
|
+
ranges.push({ start: s0, end: s1 + l1, kind: "span" });
|
|
5325
|
+
r = j + 1;
|
|
5326
|
+
}
|
|
5327
|
+
};
|
|
5328
|
+
let pos = 0;
|
|
5329
|
+
while (pos <= n) {
|
|
5330
|
+
const nl = text.indexOf("\n", pos);
|
|
5331
|
+
const lineEnd = nl === -1 ? n : nl;
|
|
5332
|
+
const line = text.slice(pos, lineEnd);
|
|
5333
|
+
let indent = 0;
|
|
5334
|
+
while (indent < line.length && line[indent] === " ") indent++;
|
|
5335
|
+
const body = line.slice(indent);
|
|
5336
|
+
if (fence) {
|
|
5337
|
+
const trimmed = trimFenceLine(line);
|
|
5338
|
+
let closes = false;
|
|
5339
|
+
if (fence.char === "`") {
|
|
5340
|
+
const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
|
|
5341
|
+
if (kind === "open-nested") fence.depth++;
|
|
5342
|
+
else if (kind === "close-nested") fence.depth--;
|
|
5343
|
+
else if (kind === "close-outer") closes = indent <= 3;
|
|
5344
|
+
} else {
|
|
5345
|
+
let t = 0;
|
|
5346
|
+
while (t < trimmed.length && trimmed[t] === "~") t++;
|
|
5347
|
+
closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
|
|
5348
|
+
}
|
|
5349
|
+
if (closes) {
|
|
5350
|
+
ranges.push({ start: fence.start, end: lineEnd, kind: "fence" });
|
|
5351
|
+
fence = null;
|
|
5352
|
+
}
|
|
5353
|
+
} else {
|
|
5354
|
+
const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
|
|
5355
|
+
const info = opener ? opener[2] : "";
|
|
5356
|
+
if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
|
|
5357
|
+
if (para !== -1) spans(para, pos);
|
|
5358
|
+
para = -1;
|
|
5359
|
+
const char = opener[1][0];
|
|
5360
|
+
const parsed = char === "`" ? parseFenceOpener(line) : null;
|
|
5361
|
+
fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
|
|
5362
|
+
} else if (body.trim() === "") {
|
|
5363
|
+
if (para !== -1) spans(para, pos);
|
|
5364
|
+
para = -1;
|
|
5365
|
+
} else if (para === -1) para = pos;
|
|
5366
|
+
}
|
|
5367
|
+
if (nl === -1) break;
|
|
5368
|
+
pos = nl + 1;
|
|
5369
|
+
}
|
|
5370
|
+
if (fence) ranges.push({ start: fence.start, end: n, kind: "fence" });
|
|
5371
|
+
else if (para !== -1) spans(para, n);
|
|
5372
|
+
return ranges.sort((a, b) => a.start - b.start);
|
|
5373
|
+
}
|
|
5374
|
+
function pendingCodeSpanStart(text) {
|
|
5375
|
+
const n = text.length;
|
|
5376
|
+
if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
|
|
5377
|
+
let fence = null;
|
|
5378
|
+
let para = -1;
|
|
5379
|
+
let pos = 0;
|
|
5380
|
+
while (pos <= n) {
|
|
5381
|
+
const nl = text.indexOf("\n", pos);
|
|
5382
|
+
const lineEnd = nl === -1 ? n : nl;
|
|
5383
|
+
const line = text.slice(pos, lineEnd);
|
|
5384
|
+
let indent = 0;
|
|
5385
|
+
while (indent < line.length && line[indent] === " ") indent++;
|
|
5386
|
+
const body = line.slice(indent);
|
|
5387
|
+
if (fence) {
|
|
5388
|
+
const trimmed = trimFenceLine(line);
|
|
5389
|
+
let closes = false;
|
|
5390
|
+
if (fence.char === "`") {
|
|
5391
|
+
const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
|
|
5392
|
+
if (kind === "open-nested") fence.depth++;
|
|
5393
|
+
else if (kind === "close-nested") fence.depth--;
|
|
5394
|
+
else if (kind === "close-outer") closes = indent <= 3;
|
|
5395
|
+
} else {
|
|
5396
|
+
let t = 0;
|
|
5397
|
+
while (t < trimmed.length && trimmed[t] === "~") t++;
|
|
5398
|
+
closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
|
|
5399
|
+
}
|
|
5400
|
+
if (closes) fence = null;
|
|
5401
|
+
} else {
|
|
5402
|
+
const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
|
|
5403
|
+
const info = opener ? opener[2] : "";
|
|
5404
|
+
if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
|
|
5405
|
+
para = -1;
|
|
5406
|
+
const char = opener[1][0];
|
|
5407
|
+
const parsed = char === "`" ? parseFenceOpener(line) : null;
|
|
5408
|
+
fence = { char, len: opener[1].length, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
|
|
5409
|
+
} else if (body.trim() === "") {
|
|
5410
|
+
if (!(nl === -1 && line === "")) para = -1;
|
|
5411
|
+
} else if (para === -1) para = pos;
|
|
5412
|
+
}
|
|
5413
|
+
if (nl === -1) break;
|
|
5414
|
+
pos = nl + 1;
|
|
5415
|
+
}
|
|
5416
|
+
if (fence || para === -1) return -1;
|
|
5417
|
+
const runs = [];
|
|
5418
|
+
for (let k = para; k < n; ) {
|
|
5419
|
+
if (text.charCodeAt(k) !== 96) {
|
|
5420
|
+
k++;
|
|
5421
|
+
continue;
|
|
5422
|
+
}
|
|
5423
|
+
let len = 1;
|
|
5424
|
+
while (k + len < n && text.charCodeAt(k + len) === 96) len++;
|
|
5425
|
+
runs.push([k, len]);
|
|
5426
|
+
k += len;
|
|
5427
|
+
}
|
|
5428
|
+
const growing = (r) => {
|
|
5429
|
+
const [start, len] = runs[r];
|
|
5430
|
+
return start + len === n;
|
|
5431
|
+
};
|
|
5432
|
+
for (let r = 0; r < runs.length; ) {
|
|
5433
|
+
if (growing(r)) return runs[r][0];
|
|
5434
|
+
const len = runs[r][1];
|
|
5435
|
+
let j = r + 1;
|
|
5436
|
+
while (j < runs.length && (runs[j][1] !== len || growing(j))) j++;
|
|
5437
|
+
if (j >= runs.length) return runs[r][0];
|
|
5438
|
+
r = j + 1;
|
|
5439
|
+
}
|
|
5440
|
+
return -1;
|
|
5441
|
+
}
|
|
5442
|
+
|
|
5291
5443
|
// source/math-pairs.ts
|
|
5292
5444
|
var MAX_MATH_SPAN = 600;
|
|
5293
5445
|
var STRUCTURAL_MARKDOWN = /\]\(|https?:\/\/|\*\*|(?:^|\n)[ \t]{0,3}#{1,6}[ \t]|(?:^|\n)[ \t]*[-*+][ \t]+|(?:^|\n)[ \t]*\d+[.)][ \t]/;
|
|
5294
5446
|
var LATEX_COMMAND = /\\[a-zA-Z]/;
|
|
5295
5447
|
var PROSE_WORD = /[A-Za-z]{3,}/g;
|
|
5296
5448
|
var PROSE_WORD_LIMIT = 6;
|
|
5449
|
+
function isMathPlaceholder(inner) {
|
|
5450
|
+
return /^[\s.…·⋯_-]*$/.test(inner) && /[.…·⋯]/.test(inner);
|
|
5451
|
+
}
|
|
5297
5452
|
function opensFlowBlock(text, open) {
|
|
5298
5453
|
for (let k = open - 1; k >= 0 && text[k] !== "\n"; k--) if (text[k] !== " " && text[k] !== " ") return false;
|
|
5299
5454
|
for (let k = open + 2; k < text.length && text[k] !== "\n"; k++) if (text[k] !== " " && text[k] !== " " && text[k] !== "\r") return false;
|
|
@@ -5311,15 +5466,7 @@ function looksLikeDisplayMath(inner, flow = false) {
|
|
|
5311
5466
|
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
|
|
5312
5467
|
}
|
|
5313
5468
|
function codeRanges(text) {
|
|
5314
|
-
|
|
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++;
|
|
5320
|
-
}
|
|
5321
|
-
}
|
|
5322
|
-
return ranges.sort((a, b) => a[0] - b[0]);
|
|
5469
|
+
return findCodeRanges(text).map((r) => [r.start, r.end]);
|
|
5323
5470
|
}
|
|
5324
5471
|
function pendingDisplayMathOpener(text) {
|
|
5325
5472
|
if (!text.includes("$$")) return -1;
|
|
@@ -5341,6 +5488,10 @@ function pendingDisplayMathOpener(text) {
|
|
|
5341
5488
|
const open = tokens[j];
|
|
5342
5489
|
const close = tokens[j + 1];
|
|
5343
5490
|
if (close === void 0) break;
|
|
5491
|
+
if (isMathPlaceholder(text.slice(open + 2, close))) {
|
|
5492
|
+
j += 2;
|
|
5493
|
+
continue;
|
|
5494
|
+
}
|
|
5344
5495
|
if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
|
|
5345
5496
|
paired.add(open);
|
|
5346
5497
|
paired.add(close);
|
|
@@ -5385,6 +5536,10 @@ function pairDisplayMath(text) {
|
|
|
5385
5536
|
const open = tokens[j];
|
|
5386
5537
|
const close = tokens[j + 1];
|
|
5387
5538
|
if (close === void 0) break;
|
|
5539
|
+
if (isMathPlaceholder(text.slice(open + 2, close))) {
|
|
5540
|
+
j += 2;
|
|
5541
|
+
continue;
|
|
5542
|
+
}
|
|
5388
5543
|
if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
|
|
5389
5544
|
pairs.set(open, close);
|
|
5390
5545
|
j += 2;
|
|
@@ -5722,6 +5877,27 @@ var Tokenizer = class {
|
|
|
5722
5877
|
this.mathPairs = pairDisplayMath(text);
|
|
5723
5878
|
this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
|
|
5724
5879
|
if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
|
|
5880
|
+
if (streaming && text.includes("$$")) {
|
|
5881
|
+
const span = pendingCodeSpanStart(text);
|
|
5882
|
+
if (span !== -1) {
|
|
5883
|
+
const firstToken = text.indexOf("$$", span);
|
|
5884
|
+
if (firstToken !== -1) this.provisionalFrom = Math.min(this.provisionalFrom, firstToken);
|
|
5885
|
+
for (const [open, close] of this.mathPairs) {
|
|
5886
|
+
if (close >= span) this.provisionalFrom = Math.min(this.provisionalFrom, open);
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
for (const pad of ["`", "``", "```", "~~~"]) {
|
|
5890
|
+
const alt = pairDisplayMath(text + pad);
|
|
5891
|
+
for (const [open, close] of this.mathPairs) {
|
|
5892
|
+
if (alt.get(open) !== close) this.provisionalFrom = Math.min(this.provisionalFrom, open);
|
|
5893
|
+
}
|
|
5894
|
+
for (const [open, close] of alt) {
|
|
5895
|
+
if (close < text.length && this.mathPairs.get(open) !== close) {
|
|
5896
|
+
this.provisionalFrom = Math.min(this.provisionalFrom, open);
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5725
5901
|
let start = 0;
|
|
5726
5902
|
for (let i = 0; i < text.length; i++) {
|
|
5727
5903
|
if (text.charCodeAt(i) === 10) {
|
|
@@ -5762,13 +5938,17 @@ var Tokenizer = class {
|
|
|
5762
5938
|
/**
|
|
5763
5939
|
* Streaming: an open last line that begins with `{` is undecided — each new
|
|
5764
5940
|
* byte can balance or unbalance its braces and flip the whole line between
|
|
5765
|
-
* prose and a JSON island — so
|
|
5941
|
+
* prose and a JSON island — and so is a line that is exactly one allowed
|
|
5942
|
+
* HTML tag so far; both are provisional until they end (G5).
|
|
5766
5943
|
*/
|
|
5767
5944
|
markOpenJsonTail() {
|
|
5768
5945
|
const last = this.lineStarts.length - 1;
|
|
5769
5946
|
if (!this.isOpenTail(last)) return;
|
|
5770
5947
|
const start = this.lineStarts[last];
|
|
5771
|
-
|
|
5948
|
+
const t = this.lineText(last).trim();
|
|
5949
|
+
const oneTag = t.startsWith("<") ? readXmlTag(t, 0) : null;
|
|
5950
|
+
const loneTag = !!oneTag && oneTag.raw === t && ALLOWED_RAW_HTML_TAGS.has(oneTag.tagName.toLowerCase());
|
|
5951
|
+
if (t.startsWith("{") || loneTag) {
|
|
5772
5952
|
this.provisionalFrom = Math.min(this.provisionalFrom, start);
|
|
5773
5953
|
}
|
|
5774
5954
|
}
|
|
@@ -6061,11 +6241,17 @@ var Tokenizer = class {
|
|
|
6061
6241
|
last = k;
|
|
6062
6242
|
continue;
|
|
6063
6243
|
}
|
|
6244
|
+
if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
|
|
6064
6245
|
if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
|
|
6065
|
-
if (this.isOpenTail(k)) break;
|
|
6066
6246
|
last = k;
|
|
6067
6247
|
}
|
|
6068
|
-
|
|
6248
|
+
const end = this.contentEnds[last];
|
|
6249
|
+
if (this.paragraphMayGrow(end)) return this.heldFootnote(p, open[1]);
|
|
6250
|
+
return this.island("footnote_def", p, end, true, { id: open[1] });
|
|
6251
|
+
}
|
|
6252
|
+
/** A footnote whose continuation is still arriving: open to the end of the text. */
|
|
6253
|
+
heldFootnote(p, id) {
|
|
6254
|
+
return this.island("footnote_def", p, this.n, false, { id });
|
|
6069
6255
|
}
|
|
6070
6256
|
/**
|
|
6071
6257
|
* Backtick fence — the renderer splitter's close rules: JSON-string aware for
|
|
@@ -6309,7 +6495,12 @@ var Tokenizer = class {
|
|
|
6309
6495
|
return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
|
|
6310
6496
|
}
|
|
6311
6497
|
const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
|
|
6312
|
-
const
|
|
6498
|
+
const loneTag = atParaStart && t.trimEnd() === tag.raw;
|
|
6499
|
+
if (loneTag && this.isOpenTail(li)) {
|
|
6500
|
+
this.provisionalFrom = Math.min(this.provisionalFrom, p);
|
|
6501
|
+
return null;
|
|
6502
|
+
}
|
|
6503
|
+
const typeSeven = loneTag;
|
|
6313
6504
|
if (!typeSix && !typeSeven) return null;
|
|
6314
6505
|
let end = this.contentEnds[li];
|
|
6315
6506
|
for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
|
|
@@ -6401,10 +6592,6 @@ var Tokenizer = class {
|
|
|
6401
6592
|
let breakLine = -1;
|
|
6402
6593
|
for (let k = li + 1; k < this.lineCount; k++) {
|
|
6403
6594
|
if (this.lineIsBlank(k)) break;
|
|
6404
|
-
if (this.lineStarts[k] >= this.provisionalFrom) {
|
|
6405
|
-
end = this.n;
|
|
6406
|
-
break;
|
|
6407
|
-
}
|
|
6408
6595
|
if (this.detectBlock(this.lineStarts[k], k, false)) {
|
|
6409
6596
|
breakLine = k;
|
|
6410
6597
|
break;
|
|
@@ -6547,7 +6734,11 @@ var Tokenizer = class {
|
|
|
6547
6734
|
}
|
|
6548
6735
|
const end = singleDollarMathEnd(text, i, pe);
|
|
6549
6736
|
this.tick(end > 0 ? end - i : Math.min(403, pe - i));
|
|
6550
|
-
if (end > 0 &&
|
|
6737
|
+
if (end > 0 && this.streaming && end === this.n) {
|
|
6738
|
+
this.provisionalFrom = Math.min(this.provisionalFrom, i);
|
|
6739
|
+
return { inlines, breakIsland: null, orphan: false };
|
|
6740
|
+
}
|
|
6741
|
+
if (end > 0) {
|
|
6551
6742
|
inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
|
|
6552
6743
|
i = end;
|
|
6553
6744
|
continue;
|
|
@@ -7101,6 +7292,7 @@ exports.executesAtOutputRoot = executesAtOutputRoot;
|
|
|
7101
7292
|
exports.fenceDiscriminator = fenceDiscriminator;
|
|
7102
7293
|
exports.fenceNestsInnerFences = fenceNestsInnerFences;
|
|
7103
7294
|
exports.fieldsToDbPayload = fieldsToDbPayload;
|
|
7295
|
+
exports.findCodeRanges = findCodeRanges;
|
|
7104
7296
|
exports.fingerprintText = fingerprintText;
|
|
7105
7297
|
exports.formatBlockLabel = formatBlockLabel;
|
|
7106
7298
|
exports.getParseSession = getParseSession;
|
|
@@ -7117,6 +7309,7 @@ exports.isIrEnvelopeCache = isIrEnvelopeCache;
|
|
|
7117
7309
|
exports.isJsonAnyField = isJsonAnyField;
|
|
7118
7310
|
exports.isKindDirective = isKindDirective;
|
|
7119
7311
|
exports.isLegacyShell = isLegacyShell;
|
|
7312
|
+
exports.isMathPlaceholder = isMathPlaceholder;
|
|
7120
7313
|
exports.isProvisionalKind = isProvisionalKind;
|
|
7121
7314
|
exports.isReservedDirectiveSlug = isReservedDirectiveSlug;
|
|
7122
7315
|
exports.isScalarArrayType = isScalarArrayType;
|