@ai-matrx/content-ir 0.21.0 → 0.21.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,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.2
4
+
5
+ ### `/source`: a ```json fence's string state never crosses a line end (never-raw R11 H2)
6
+
7
+ - `FenceReader` (behind `closeFence`, `fenceLineKinds`, `findCodeRanges`, the XML / directive balance walkers) read
8
+ JSON-string state across lines inside a ```json fence. A JSON string never holds a raw newline, so a body line
9
+ that ends inside one (`{\"__kind\":\"note\"}` — escaped quotes, a cut value) swallowed the closing ``` and every
10
+ following list item / quoted line. The state is now read per line; a backtick run inside a string on the same line
11
+ is still text.
12
+
13
+ ### Consumer action
14
+
15
+ - None required. matrx-frontend may drop its splitter workaround (`jsonLineForFenceReader` in
16
+ content-splitter-core.ts) once on 0.21.2.
17
+
18
+ ## 0.21.1
19
+
20
+ ### `/source`: the tokenizer locks what the renderer draws (fuzz `source-covers-renderer`)
21
+
22
+ - A `~~~` island now ends at its closer. The 0.14.2 "renderer overflow" assumed the renderer did not know `~~~`
23
+ (it has since matrx-frontend A4, 2026-09-30), and swallowed the next fence's opener into the tilde island,
24
+ leaving a following ````` ````md ````` block editable.
25
+ - A JSON-family fence (unlabelled / json / jsonc / json5) opened on a quoted line (`> ```) is an island up to its
26
+ own closer, which may carry no `>`: the renderer lifts such a region out of its quote (V1 quoted-kind-lift). A
27
+ quoted fence of another language stays prose (source).
28
+
29
+ ### Consumer action
30
+
31
+ - None required.
32
+
3
33
  ## 0.21.0
4
34
 
5
35
  ### `/source`: the markdown text helpers move in from matrx-frontend (chat-package-move P14)
package/dist/index.cjs CHANGED
@@ -5487,9 +5487,9 @@ function fenceOpenerOf(content) {
5487
5487
  function opensStrictFence(line) {
5488
5488
  return fenceOpenerOf(line) !== null;
5489
5489
  }
5490
+ var LINE_START = { inString: false, escaped: false };
5490
5491
  var FenceReader = class {
5491
5492
  opener;
5492
- state = { inString: false, escaped: false };
5493
5493
  depth = 0;
5494
5494
  /** A nested fence was opened at some point. */
5495
5495
  sawNested = false;
@@ -5510,17 +5510,8 @@ var FenceReader = class {
5510
5510
  if (this.tildeCloser) return this.tildeCloser.test(line);
5511
5511
  const trimmedLine = trimFenceLine(line);
5512
5512
  const at = line.indexOf("```");
5513
- if (at === -1) {
5514
- if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5515
- return false;
5516
- }
5517
- if (this.isJson) {
5518
- const probe = jsonStringState(line, 0, at, this.state);
5519
- if (probe.inString) {
5520
- this.state = jsonStringState(line, at, line.length, probe);
5521
- return false;
5522
- }
5523
- }
5513
+ if (at === -1) return false;
5514
+ if (this.isJson && jsonStringState(line, 0, at, LINE_START).inString) return false;
5524
5515
  if (trimmedLine.startsWith("```")) {
5525
5516
  const kind = classifyInnerFenceLine(trimmedLine, this.opener.ticks, this.nesting, this.depth);
5526
5517
  if (kind === "close-outer") return true;
@@ -5534,7 +5525,6 @@ var FenceReader = class {
5534
5525
  const after = trimFenceLine(line.slice(at + closeTicks));
5535
5526
  if (closeTicks >= this.opener.ticks && after === "" && this.depth === 0) return true;
5536
5527
  }
5537
- if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5538
5528
  return false;
5539
5529
  }
5540
5530
  };
@@ -6418,6 +6408,8 @@ function findTableEnd(lines, start) {
6418
6408
  }
6419
6409
 
6420
6410
  // source/tokenize.ts
6411
+ var QUOTE_PREFIX = /^ {0,3}>(?: ?>)* ?/;
6412
+ var QUOTED_JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
6421
6413
  var NOT_WHITESPACE = /\S/;
6422
6414
  var DIRECTIVE_OPEN = /^(:{2,})([A-Za-z][\w-]*)/;
6423
6415
  var MKDOCS_OPEN = /^(!!!|\?\?\?\+?)[ \t]+([A-Za-z][\w-]*)/;
@@ -6907,6 +6899,8 @@ var Tokenizer = class {
6907
6899
  if (indent <= 3 && !listNested && t.startsWith(">")) {
6908
6900
  const callout = this.callout(p, li, t);
6909
6901
  if (callout) return callout;
6902
+ const lifted = this.quotedJsonFence(p, li, seg);
6903
+ if (lifted) return lifted;
6910
6904
  }
6911
6905
  if (atParaStart && indent <= 3 && t.startsWith("[^")) {
6912
6906
  const footnote = this.footnoteDefinition(p, li, t);
@@ -7102,6 +7096,42 @@ var Tokenizer = class {
7102
7096
  streaming: this.streaming
7103
7097
  };
7104
7098
  }
7099
+ /**
7100
+ * A JSON-family fence (unlabelled, json, jsonc, json5) opening on a quoted line. The renderer LIFTS it out
7101
+ * of the quote (matrx-frontend V1, never-raw law): the quote prefix is removed from every line of the
7102
+ * region, so the region is an ordinary top-level fence — drawn as code up to its own closer, which may sit on
7103
+ * a line that carries no `>` at all. The editor must lock at least what the renderer draws, so the island
7104
+ * runs from the quoted opener line to that closer (found on the lines with their quote prefix removed).
7105
+ */
7106
+ quotedJsonFence(p, li, seg) {
7107
+ const m = QUOTE_PREFIX.exec(seg);
7108
+ if (!m) return null;
7109
+ const opener = fenceOpenerOf(seg.slice(m[0].length));
7110
+ if (!opener || opener.char !== "`" || !QUOTED_JSON_FENCE_LANGS.has(opener.lang.toLowerCase())) return null;
7111
+ const base2 = this.fenceLines;
7112
+ const lines = {
7113
+ ...base2,
7114
+ line: (k) => {
7115
+ const text = base2.line(k);
7116
+ if (text === null) return null;
7117
+ const q = QUOTE_PREFIX.exec(text);
7118
+ return q ? text.slice(q[0].length) : text;
7119
+ }
7120
+ };
7121
+ let first = li;
7122
+ while (first > 0 && li - first < 400 && QUOTE_PREFIX.test(this.lineText(first - 1))) first--;
7123
+ for (let k = first; k < li; k++) {
7124
+ const o = fenceOpenerOf(lines.line(k) ?? "");
7125
+ if (!o || o.char !== "`" || QUOTED_JSON_FENCE_LANGS.has(o.lang.toLowerCase())) continue;
7126
+ const c = closeFence(lines, k, o);
7127
+ if (!c.closed || c.line >= li) return null;
7128
+ k = c.line;
7129
+ }
7130
+ const end = closeFence(lines, li, opener);
7131
+ const meta = { fence: "`", ticks: opener.ticks, lang: opener.lang };
7132
+ if (end.closed) return this.island("fence", p, this.contentEnds[end.line], true, meta);
7133
+ return this.island("fence", p, this.n, false, meta);
7134
+ }
7105
7135
  /**
7106
7136
  * Backtick fence — THE one closer (`closeFence`, source/code-ranges.ts): the
7107
7137
  * nested-fence rule for markdown fences with the strict-CommonMark retry, the
@@ -7134,44 +7164,14 @@ var Tokenizer = class {
7134
7164
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
7135
7165
  const close = closeFence(this.fenceLines, li, opener);
7136
7166
  if (close.closed) {
7137
- const tildeEnd = this.contentEnds[close.line];
7138
- const end = this.rendererOverflow(li + 1, close.line, tildeEnd);
7139
- return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
7167
+ return this.island("fence", p, this.contentEnds[close.line], true, meta);
7140
7168
  }
7141
7169
  return this.island("fence", p, this.n, false, meta);
7142
7170
  }
7143
- /** Completion of the last `rendererOverflow` that ran to the end of the text. */
7144
- overflowComplete = true;
7145
- /**
7146
- * The renderer's splitter does not know ~~~ fences: it reads the lines inside
7147
- * one as ordinary lines, so a ```` ``` ```` fence (or an XML section, a JSON
7148
- * block…) that STARTS inside the tilde fence can run past its closer. The
7149
- * editor must lock at least what the renderer draws (verify-RC-B3 residual
7150
- * R5), so a tilde island extends over every block the renderer would detect
7151
- * starting inside it. Returns the island's end.
7152
- */
7153
- rendererOverflow(firstLine, closeLine, tildeEnd) {
7154
- let end = tildeEnd;
7155
- this.overflowComplete = true;
7156
- for (let j = firstLine; j <= closeLine && j < this.lineCount; j++) {
7157
- const start = this.lineStarts[j];
7158
- if (this.lineIsBlank(j)) continue;
7159
- if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
7160
- const inner = this.detectBlockUncached(start, j, false);
7161
- if (!inner) continue;
7162
- if (this.streaming && inner.end >= tildeEnd && this.paragraphMayGrow(inner.end)) {
7163
- this.overflowComplete = false;
7164
- return this.n;
7165
- }
7166
- if (inner.end > end) {
7167
- end = inner.end;
7168
- this.overflowComplete = inner.complete;
7169
- }
7170
- const lastLine = this.lineOf(Math.max(start, inner.end - 1));
7171
- if (lastLine > j) j = lastLine;
7172
- }
7173
- return end;
7174
- }
7171
+ // A tilde island ends at its closer. The renderer's splitter treats `~~~` as a real fence since
7172
+ // matrx-frontend A4 (2026-09-30): the lines inside are code, so nothing that starts inside one runs
7173
+ // past the closer (the 0.14.2 "overflow" assumed the renderer did not know ~~~ and, kept, swallowed the
7174
+ // next fence's opener into the tilde island: a ````md block after a ~~~ block was left unlocked).
7175
7175
  /**
7176
7176
  * A line-leading `$$` that OPENS a math pair (source/math-pairs.ts — the
7177
7177
  * core's rule). A closer, an unpaired `$$`, or a prose pair is never a block: