@ai-matrx/content-ir 0.20.1 → 0.21.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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.1
4
+
5
+ ### `/source`: the tokenizer locks what the renderer draws (fuzz `source-covers-renderer`)
6
+
7
+ - A `~~~` island now ends at its closer. The 0.14.2 "renderer overflow" assumed the renderer did not know `~~~`
8
+ (it has since matrx-frontend A4, 2026-09-30), and swallowed the next fence's opener into the tilde island,
9
+ leaving a following ````` ````md ````` block editable.
10
+ - A JSON-family fence (unlabelled / json / jsonc / json5) opened on a quoted line (`> ```) is an island up to its
11
+ own closer, which may carry no `>`: the renderer lifts such a region out of its quote (V1 quoted-kind-lift). A
12
+ quoted fence of another language stays prose (source).
13
+
14
+ ### Consumer action
15
+
16
+ - None required.
17
+
18
+ ## 0.21.0
19
+
20
+ ### `/source`: the markdown text helpers move in from matrx-frontend (chat-package-move P14)
21
+
22
+ - New on `@ai-matrx/content-ir/source`, unchanged in behavior: `unwrapCodeSpans`, `removeCodeSpans`,
23
+ `replaceFences`, `soleFence`; `markdownToPlainText`, `mathToReadable`, `unwrapKindEnvelopes`; `cleanMarkdown`;
24
+ `FENCE_META_KEY`, `splitFenceInfo`, `parseFenceMeta`, `parseLineRanges` (+ `FenceInfo`, `FenceMeta`);
25
+ `DIRECTIVE_CONTAINER_OPEN`, `DirectiveContainerTracker`; `TITLED_IMAGE_LINE`; `isInsideInlineCode`,
26
+ `indexOutsideInlineCode`, `hasUnclosedBacktickRun`; `initialXmlBalance`, `findBalancedXmlClose`
27
+ (+ `XmlBalanceState`, `XmlBalanceOptions`); `findWholeTableStart`, `startsLikeTableRow`,
28
+ `isGrowingDelimiterRow`, `trailingTableStart`.
29
+ - `splitFenceInfo` omits `language` / `meta` when empty instead of setting them to `undefined`.
30
+
31
+ ### Consumer action
32
+
33
+ - matrx-frontend: import these from `@ai-matrx/content-ir/source` (the app's old modules re-export or are gone).
34
+
3
35
  ## 0.20.1
4
36
 
5
37
  ### `humanizeIdentifier`: already-human text, plurals, null
package/dist/index.cjs CHANGED
@@ -6418,6 +6418,8 @@ function findTableEnd(lines, start) {
6418
6418
  }
6419
6419
 
6420
6420
  // source/tokenize.ts
6421
+ var QUOTE_PREFIX = /^ {0,3}>(?: ?>)* ?/;
6422
+ var QUOTED_JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
6421
6423
  var NOT_WHITESPACE = /\S/;
6422
6424
  var DIRECTIVE_OPEN = /^(:{2,})([A-Za-z][\w-]*)/;
6423
6425
  var MKDOCS_OPEN = /^(!!!|\?\?\?\+?)[ \t]+([A-Za-z][\w-]*)/;
@@ -6907,6 +6909,8 @@ var Tokenizer = class {
6907
6909
  if (indent <= 3 && !listNested && t.startsWith(">")) {
6908
6910
  const callout = this.callout(p, li, t);
6909
6911
  if (callout) return callout;
6912
+ const lifted = this.quotedJsonFence(p, li, seg);
6913
+ if (lifted) return lifted;
6910
6914
  }
6911
6915
  if (atParaStart && indent <= 3 && t.startsWith("[^")) {
6912
6916
  const footnote = this.footnoteDefinition(p, li, t);
@@ -7102,6 +7106,42 @@ var Tokenizer = class {
7102
7106
  streaming: this.streaming
7103
7107
  };
7104
7108
  }
7109
+ /**
7110
+ * A JSON-family fence (unlabelled, json, jsonc, json5) opening on a quoted line. The renderer LIFTS it out
7111
+ * of the quote (matrx-frontend V1, never-raw law): the quote prefix is removed from every line of the
7112
+ * region, so the region is an ordinary top-level fence — drawn as code up to its own closer, which may sit on
7113
+ * a line that carries no `>` at all. The editor must lock at least what the renderer draws, so the island
7114
+ * runs from the quoted opener line to that closer (found on the lines with their quote prefix removed).
7115
+ */
7116
+ quotedJsonFence(p, li, seg) {
7117
+ const m = QUOTE_PREFIX.exec(seg);
7118
+ if (!m) return null;
7119
+ const opener = fenceOpenerOf(seg.slice(m[0].length));
7120
+ if (!opener || opener.char !== "`" || !QUOTED_JSON_FENCE_LANGS.has(opener.lang.toLowerCase())) return null;
7121
+ const base2 = this.fenceLines;
7122
+ const lines = {
7123
+ ...base2,
7124
+ line: (k) => {
7125
+ const text = base2.line(k);
7126
+ if (text === null) return null;
7127
+ const q = QUOTE_PREFIX.exec(text);
7128
+ return q ? text.slice(q[0].length) : text;
7129
+ }
7130
+ };
7131
+ let first = li;
7132
+ while (first > 0 && li - first < 400 && QUOTE_PREFIX.test(this.lineText(first - 1))) first--;
7133
+ for (let k = first; k < li; k++) {
7134
+ const o = fenceOpenerOf(lines.line(k) ?? "");
7135
+ if (!o || o.char !== "`" || QUOTED_JSON_FENCE_LANGS.has(o.lang.toLowerCase())) continue;
7136
+ const c = closeFence(lines, k, o);
7137
+ if (!c.closed || c.line >= li) return null;
7138
+ k = c.line;
7139
+ }
7140
+ const end = closeFence(lines, li, opener);
7141
+ const meta = { fence: "`", ticks: opener.ticks, lang: opener.lang };
7142
+ if (end.closed) return this.island("fence", p, this.contentEnds[end.line], true, meta);
7143
+ return this.island("fence", p, this.n, false, meta);
7144
+ }
7105
7145
  /**
7106
7146
  * Backtick fence — THE one closer (`closeFence`, source/code-ranges.ts): the
7107
7147
  * nested-fence rule for markdown fences with the strict-CommonMark retry, the
@@ -7134,44 +7174,14 @@ var Tokenizer = class {
7134
7174
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
7135
7175
  const close = closeFence(this.fenceLines, li, opener);
7136
7176
  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);
7177
+ return this.island("fence", p, this.contentEnds[close.line], true, meta);
7140
7178
  }
7141
7179
  return this.island("fence", p, this.n, false, meta);
7142
7180
  }
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
- }
7181
+ // A tilde island ends at its closer. The renderer's splitter treats `~~~` as a real fence since
7182
+ // matrx-frontend A4 (2026-09-30): the lines inside are code, so nothing that starts inside one runs
7183
+ // past the closer (the 0.14.2 "overflow" assumed the renderer did not know ~~~ and, kept, swallowed the
7184
+ // next fence's opener into the tilde island: a ````md block after a ~~~ block was left unlocked).
7175
7185
  /**
7176
7186
  * A line-leading `$$` that OPENS a math pair (source/math-pairs.ts — the
7177
7187
  * core's rule). A closer, an unpaired `$$`, or a prose pair is never a block:
@@ -8087,6 +8097,424 @@ function resolveReferenceLinks(fragment, defs) {
8087
8097
  return resolved.replace(new RegExp(`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`, "g"), (_m, i) => spans[Number(i)] ?? "");
8088
8098
  }
8089
8099
 
8100
+ // source/code-range-edits.ts
8101
+ function unwrapCodeSpans(text) {
8102
+ if (!text.includes("`")) return text;
8103
+ return mapCodeRanges(text, (range, raw) => range.kind === "span" ? codeSpanText(raw) : raw);
8104
+ }
8105
+ function removeCodeSpans(text) {
8106
+ if (!text.includes("`")) return text;
8107
+ return mapCodeRanges(text, (range, raw) => range.kind === "span" ? "" : raw);
8108
+ }
8109
+ function replaceFences(text, fn) {
8110
+ if (!text.includes("```") && !text.includes("~~~")) return text;
8111
+ return mapCodeRanges(text, (range, raw) => {
8112
+ if (range.kind !== "fence") return raw;
8113
+ const parts = fenceParts(raw);
8114
+ return fn({ lang: parts?.opener.lang ?? "", body: parts?.body ?? raw, closed: parts?.closed ?? false, raw });
8115
+ });
8116
+ }
8117
+ function soleFence(text, options = {}) {
8118
+ const trimmed = text.trim();
8119
+ if (!trimmed.startsWith("`") && !trimmed.startsWith("~")) return null;
8120
+ const ranges = findCodeRanges(trimmed);
8121
+ const only = ranges.length === 1 ? ranges[0] : void 0;
8122
+ if (!only || only.kind !== "fence" || only.start !== 0 || only.end !== trimmed.length) return null;
8123
+ const parts = fenceParts(trimmed);
8124
+ if (!parts || !parts.closed && !options.allowUnclosed) return null;
8125
+ return { lang: parts.opener.lang, body: parts.body, closed: parts.closed };
8126
+ }
8127
+
8128
+ // source/plain-text.ts
8129
+ var INLINE_PATTERNS = [
8130
+ // [label](href) → label. Runs first so emphasis inside a label still unwraps.
8131
+ [/\[([^\]]+)\]\([^)]*\)/g, "$1"],
8132
+ [/\*\*\*(?=\S)([\s\S]*?\S)\*\*\*/g, "$1"],
8133
+ [/___(?=\S)([\s\S]*?\S)___/g, "$1"],
8134
+ [/\*\*(?=\S)([\s\S]*?\S)\*\*/g, "$1"],
8135
+ [/__(?=\S)([\s\S]*?\S)__/g, "$1"],
8136
+ [/~~(?=\S)([\s\S]*?\S)~~/g, "$1"],
8137
+ [/\*(?=\S)([^*\n]*?\S)\*/g, "$1"],
8138
+ // Underscore italics only between word boundaries: snake_case_names are not
8139
+ // emphasis, and mangling an identifier is a worse defect than a stray marker.
8140
+ [/(^|[\s(])_(?=\S)([^_\n]*?\S)_(?=[\s).,;:!?]|$)/g, "$1$2"]
8141
+ ];
8142
+ var MATH_STRUCTURAL = [
8143
+ [/\\(?:d|t)?frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g, "$1/$2"],
8144
+ [/\\sqrt\s*\{([^{}]*)\}/g, "\u221A($1)"]
8145
+ ];
8146
+ var MATH_PATTERNS = [
8147
+ // Environments (\begin{aligned} … \end{aligned}) carry no meaning in a
8148
+ // one-line preview; drop the wrapper, keep the contents. Without this the
8149
+ // catch-all below turned them into the word "aligned" twice.
8150
+ [/\\(?:begin|end)\s*\{[^{}]*\}/g, " "],
8151
+ // A TeX line break inside a preview is just a space.
8152
+ [/\\\\/g, " "],
8153
+ // TeX spacing commands (\, \; \: thin/medium space, \! negative space).
8154
+ [/\\[,;:]/g, " "],
8155
+ [/\\!/g, ""],
8156
+ // \left\{ … \right\} — the escaped-brace forms, which the character-class
8157
+ // rule below cannot reach because the brace is preceded by a backslash.
8158
+ [/\\(?:left|right)\s*\\?([([\]|){}])/g, "$1"],
8159
+ // NOTE: every command below ends with (?![a-zA-Z]) and NOT \b. `_` is a WORD
8160
+ // character, so \b fails after `\sum_` — which silently left every
8161
+ // summation, integral and limit card reading as "sum_" / "int_".
8162
+ [/\\times(?![a-zA-Z])/g, "\xD7"],
8163
+ [/\\div(?![a-zA-Z])/g, "\xF7"],
8164
+ [/\\pm(?![a-zA-Z])/g, "\xB1"],
8165
+ [/\\cdot(?![a-zA-Z])/g, "\xB7"],
8166
+ [/\\leq(?![a-zA-Z])/g, "\u2264"],
8167
+ [/\\geq(?![a-zA-Z])/g, "\u2265"],
8168
+ [/\\neq(?![a-zA-Z])/g, "\u2260"],
8169
+ [/\\approx(?![a-zA-Z])/g, "\u2248"],
8170
+ [/\\infty(?![a-zA-Z])/g, "\u221E"],
8171
+ [/\\pi(?![a-zA-Z])/g, "\u03C0"],
8172
+ [/\\theta(?![a-zA-Z])/g, "\u03B8"],
8173
+ [/\\alpha(?![a-zA-Z])/g, "\u03B1"],
8174
+ [/\\beta(?![a-zA-Z])/g, "\u03B2"],
8175
+ [/\\Delta(?![a-zA-Z])/g, "\u0394"],
8176
+ [/\\delta(?![a-zA-Z])/g, "\u03B4"],
8177
+ [/\\sum(?![a-zA-Z])/g, "\u03A3"],
8178
+ [/\\prod(?![a-zA-Z])/g, "\u220F"],
8179
+ [/\\int(?![a-zA-Z])/g, "\u222B"],
8180
+ [/\\lim(?![a-zA-Z])/g, "lim"],
8181
+ [/\\(?:rightarrow|to)(?![a-zA-Z])/g, "\u2192"],
8182
+ // Braces that only ever grouped a script or argument: ^{2} → ^2, _{i} → _i.
8183
+ [/([_^])\s*\{([^{}]*)\}/g, "$1$2"],
8184
+ // Any remaining single-argument command: \text{x} / \mathrm{x} → x.
8185
+ [/\\[a-zA-Z]+\s*\{([^{}]*)\}/g, "$1"],
8186
+ // Bare commands we did not translate: drop the backslash, keep the word.
8187
+ [/\\([a-zA-Z]+)/g, "$1"]
8188
+ ];
8189
+ var MATH_REGIONS = [
8190
+ /\\\[([\s\S]*?)\\\]/g,
8191
+ /\\\(([\s\S]*?)\\\)/g,
8192
+ /\$\$([\s\S]*?)\$\$/g
8193
+ ];
8194
+ function mathToReadable(expression) {
8195
+ let text = expression;
8196
+ for (let pass = 0; pass < 8; pass += 1) {
8197
+ const before = text;
8198
+ for (const [pattern, replacement] of MATH_STRUCTURAL) {
8199
+ text = text.replace(pattern, replacement);
8200
+ }
8201
+ if (text === before) break;
8202
+ }
8203
+ for (const [pattern, replacement] of MATH_PATTERNS) {
8204
+ text = text.replace(pattern, replacement);
8205
+ }
8206
+ return text.trim();
8207
+ }
8208
+ function markdownToPlainText(value) {
8209
+ if (!value) return "";
8210
+ const mathChunks = [];
8211
+ let text = value;
8212
+ for (const region of MATH_REGIONS) {
8213
+ text = text.replace(region, (_match, inner) => {
8214
+ mathChunks.push(mathToReadable(inner));
8215
+ return `\uE000MATH${mathChunks.length - 1}\uE001`;
8216
+ });
8217
+ }
8218
+ for (const [pattern, replacement] of INLINE_PATTERNS) {
8219
+ text = text.replace(pattern, replacement);
8220
+ }
8221
+ text = unwrapCodeSpans(text);
8222
+ text = text.replace(
8223
+ /\uE000MATH(\d+)\uE001/g,
8224
+ (_m, index) => mathChunks[Number(index)] ?? ""
8225
+ );
8226
+ return text.replace(/[ \t]{2,}/g, " ").trim();
8227
+ }
8228
+ function unwrapKindEnvelopes(value) {
8229
+ if (!value) return "";
8230
+ if (!value.includes("<artifact")) return value;
8231
+ return tokenizeSource(value).map((block) => {
8232
+ if (block.kind !== "island" || block.islandType !== "xml_attr" && block.islandType !== "xml_container") {
8233
+ return block.raw;
8234
+ }
8235
+ const match = /^\s*<artifact\b[^>]*>([\s\S]*?)<\/artifact>\s*$/.exec(block.raw);
8236
+ return match ? (match[1] ?? "").replace(/^\n/, "").replace(/\n$/, "") : block.raw;
8237
+ }).join("");
8238
+ }
8239
+
8240
+ // source/clean-markdown.ts
8241
+ var cleanMarkdown = (text) => {
8242
+ if (!text || typeof text !== "string") return "";
8243
+ let cleanedText = text;
8244
+ const lines = cleanedText.split("\n");
8245
+ const processedLines = [];
8246
+ for (let line of lines) {
8247
+ let processedLine = line;
8248
+ if (processedLine.trim() === "") {
8249
+ processedLines.push(processedLine);
8250
+ continue;
8251
+ }
8252
+ processedLine = processedLine.replace(/^#{1,6}\s+(.+)$/, "$1");
8253
+ processedLine = processedLine.replace(/(\*\*|__)(.*?)\1/, "$2");
8254
+ processedLine = processedLine.replace(/(\*|_)(.*?)\1/, "$2");
8255
+ processedLine = unwrapCodeSpans(processedLine);
8256
+ processedLine = processedLine.replace(/\[([^\]]+)\]\(([^)]+)\)/, "$1");
8257
+ processedLine = processedLine.replace(/!\[([^\]]*)\]\(([^)]+)\)/, "$1");
8258
+ processedLine = processedLine.replace(/^>\s*(.+)$/, "$1");
8259
+ processedLine = processedLine.replace(/^(\*{3,}|-{3,}|_{3,})$/, "");
8260
+ processedLine = processedLine.replace(/^(\s*[-+*]\s+|\s*\d+\.\s+)(.+)$/, "$2");
8261
+ processedLine = processedLine.replace(/<\/?[^>]+(>|$)/g, "");
8262
+ processedLines.push(processedLine);
8263
+ }
8264
+ cleanedText = processedLines.join("\n");
8265
+ cleanedText = replaceFences(cleanedText, ({ body }) => body);
8266
+ return cleanedText;
8267
+ };
8268
+
8269
+ // source/fence-meta.ts
8270
+ var FENCE_META_KEY = "fenceMeta";
8271
+ function splitFenceInfo(info) {
8272
+ const trimmed = info.trim();
8273
+ if (!trimmed) return {};
8274
+ const firstSpace = trimmed.search(/\s/);
8275
+ let token = firstSpace === -1 ? trimmed : trimmed.slice(0, firstSpace);
8276
+ let rest = firstSpace === -1 ? "" : trimmed.slice(firstSpace).trim();
8277
+ const braceAt = token.indexOf("{");
8278
+ if (braceAt > 0) {
8279
+ rest = `${token.slice(braceAt)} ${rest}`.trim();
8280
+ token = token.slice(0, braceAt);
8281
+ }
8282
+ const colonAt = token.indexOf(":");
8283
+ if (colonAt > 0 && colonAt < token.length - 1) {
8284
+ const filename = token.slice(colonAt + 1);
8285
+ rest = `title="${filename}" ${rest}`.trim();
8286
+ token = token.slice(0, colonAt);
8287
+ }
8288
+ if (token.startsWith("{")) {
8289
+ rest = `${token} ${rest}`.trim();
8290
+ token = "";
8291
+ }
8292
+ const out = {};
8293
+ if (token) out.language = token;
8294
+ if (rest) out.meta = rest;
8295
+ return out;
8296
+ }
8297
+ var TITLE_RE = /(?:^|\s)(?:title|filename|file)=(?:"([^"]*)"|'([^']*)'|(\S+))/i;
8298
+ var RANGES_RE = /(?:^|\s)\{([\d\s,-]+)\}/;
8299
+ var LINE_NUMBERS_RE = /(?:^|\s)showLineNumbers(?:\{(\d+)\})?(?=\s|$)/;
8300
+ var MAX_HIGHLIGHT_LINES = 1e4;
8301
+ function parseFenceMeta(meta) {
8302
+ const result = { highlightLines: [] };
8303
+ if (!meta) return result;
8304
+ const title = TITLE_RE.exec(meta);
8305
+ if (title) {
8306
+ const value = (title[1] ?? title[2] ?? title[3] ?? "").trim();
8307
+ if (value) result.title = value;
8308
+ }
8309
+ const ranges = RANGES_RE.exec(meta);
8310
+ if (ranges) result.highlightLines = parseLineRanges(ranges[1] ?? "");
8311
+ const lineNumbers = LINE_NUMBERS_RE.exec(meta);
8312
+ if (lineNumbers) {
8313
+ result.showLineNumbers = true;
8314
+ if (lineNumbers[1]) {
8315
+ const start = Number.parseInt(lineNumbers[1], 10);
8316
+ if (Number.isFinite(start) && start > 0) result.startLine = start;
8317
+ }
8318
+ }
8319
+ return result;
8320
+ }
8321
+ function parseLineRanges(spec) {
8322
+ const lines = /* @__PURE__ */ new Set();
8323
+ for (const part of spec.split(",")) {
8324
+ const piece = part.trim();
8325
+ if (!piece) continue;
8326
+ const range = /^(\d+)\s*-\s*(\d+)$/.exec(piece);
8327
+ if (range) {
8328
+ const from = Number.parseInt(range[1] ?? "", 10);
8329
+ const to = Number.parseInt(range[2] ?? "", 10);
8330
+ if (from < 1 || to < from) continue;
8331
+ for (let n = from; n <= to && lines.size < MAX_HIGHLIGHT_LINES; n += 1) {
8332
+ lines.add(n);
8333
+ }
8334
+ continue;
8335
+ }
8336
+ if (/^\d+$/.test(piece)) {
8337
+ const n = Number.parseInt(piece, 10);
8338
+ if (n >= 1) lines.add(n);
8339
+ }
8340
+ }
8341
+ return [...lines].sort((a, b) => a - b);
8342
+ }
8343
+
8344
+ // source/directive-container.ts
8345
+ var DIRECTIVE_CONTAINER_OPEN = /^[ \t]{0,3}(:{3,})[A-Za-z][\w-]*/;
8346
+ var CLOSE = /^[ \t]{0,3}(:{3,})[ \t]*$/;
8347
+ var DirectiveContainerTracker = class {
8348
+ stack;
8349
+ fence = null;
8350
+ constructor(openingLine) {
8351
+ const m = DIRECTIVE_CONTAINER_OPEN.exec(openingLine);
8352
+ this.stack = [m ? m[1].length : 3];
8353
+ }
8354
+ /** Feed the next line (after the opener). True once the outermost container has closed on this line. */
8355
+ consume(line) {
8356
+ if (this.fence) {
8357
+ if (this.fence.feed(line)) this.fence = null;
8358
+ return false;
8359
+ }
8360
+ const opener = fenceOpenerOf(line);
8361
+ if (opener) {
8362
+ this.fence = new FenceReader(opener);
8363
+ return false;
8364
+ }
8365
+ const open = DIRECTIVE_CONTAINER_OPEN.exec(line);
8366
+ if (open) {
8367
+ this.stack.push(open[1].length);
8368
+ return false;
8369
+ }
8370
+ const close = CLOSE.exec(line);
8371
+ if (close && close[1].length >= (this.stack[this.stack.length - 1] ?? 3)) {
8372
+ this.stack.pop();
8373
+ return this.stack.length === 0;
8374
+ }
8375
+ return false;
8376
+ }
8377
+ };
8378
+
8379
+ // source/image-figure.ts
8380
+ var TITLED_IMAGE_LINE = /^[ \t]{0,3}!\[[^\]\n]*\]\(\s*<?[^\s)>]+>?\s+(?:"[^"\n]+"|'[^'\n]+')\s*\)[ \t]*$/;
8381
+
8382
+ // source/inline-code-span.ts
8383
+ function isInsideInlineCode(line, at) {
8384
+ let i = 0;
8385
+ while (i <= at && i < line.length) {
8386
+ if (line[i] === "\\") {
8387
+ i += 2;
8388
+ continue;
8389
+ }
8390
+ if (line[i] !== "`") {
8391
+ i += 1;
8392
+ continue;
8393
+ }
8394
+ let run = 0;
8395
+ while (line[i + run] === "`") run += 1;
8396
+ let close = -1;
8397
+ for (let j = i + run; j < line.length; ) {
8398
+ if (line[j] !== "`") {
8399
+ j += 1;
8400
+ continue;
8401
+ }
8402
+ let r = 0;
8403
+ while (line[j + r] === "`") r += 1;
8404
+ if (r === run) {
8405
+ close = j;
8406
+ break;
8407
+ }
8408
+ j += r;
8409
+ }
8410
+ if (close === -1) {
8411
+ i += run;
8412
+ continue;
8413
+ }
8414
+ if (at > i && at < close + run) return true;
8415
+ i = close + run;
8416
+ }
8417
+ return false;
8418
+ }
8419
+ function indexOutsideInlineCode(line, needle, from = 0) {
8420
+ for (let idx = line.indexOf(needle, from); idx !== -1; idx = line.indexOf(needle, idx + 1)) {
8421
+ if (!isInsideInlineCode(line, idx)) return idx;
8422
+ }
8423
+ return -1;
8424
+ }
8425
+ function hasUnclosedBacktickRun(text) {
8426
+ let i = 0;
8427
+ while (i < text.length) {
8428
+ if (text[i] === "\\") {
8429
+ i += 2;
8430
+ continue;
8431
+ }
8432
+ if (text[i] !== "`") {
8433
+ i += 1;
8434
+ continue;
8435
+ }
8436
+ let run = 0;
8437
+ while (text[i + run] === "`") run += 1;
8438
+ let close = -1;
8439
+ for (let j = i + run; j < text.length; ) {
8440
+ if (text[j] !== "`") {
8441
+ j += 1;
8442
+ continue;
8443
+ }
8444
+ let r = 0;
8445
+ while (text[j + r] === "`") r += 1;
8446
+ if (r === run) {
8447
+ close = j;
8448
+ break;
8449
+ }
8450
+ j += r;
8451
+ }
8452
+ if (close === -1) return true;
8453
+ i = close + run;
8454
+ }
8455
+ return false;
8456
+ }
8457
+
8458
+ // source/xml-tag-balance.ts
8459
+ function initialXmlBalance() {
8460
+ return { depth: 1, fence: null };
8461
+ }
8462
+ function escapeRegExp(value) {
8463
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8464
+ }
8465
+ function findBalancedXmlClose(line, tagName, state, { trackFences = true, nest = true } = {}) {
8466
+ if (state.fence) {
8467
+ if (state.fence.feed(line)) state.fence = null;
8468
+ return -1;
8469
+ }
8470
+ const opener = trackFences ? fenceOpenerOf(line) : null;
8471
+ if (opener) {
8472
+ state.fence = new FenceReader(opener);
8473
+ return -1;
8474
+ }
8475
+ const tag = new RegExp(
8476
+ `<(\\/?)${escapeRegExp(tagName)}(?=[\\s>/])[^<>]*?(\\/?)>`,
8477
+ "g"
8478
+ );
8479
+ for (const match of line.matchAll(tag)) {
8480
+ const closing = match[1] === "/";
8481
+ const selfClosing = match[2] === "/";
8482
+ if (closing) {
8483
+ state.depth -= 1;
8484
+ if (state.depth === 0) return match.index ?? -1;
8485
+ } else if (nest && !selfClosing && line.slice(0, match.index).trim() === "") {
8486
+ state.depth += 1;
8487
+ }
8488
+ }
8489
+ return -1;
8490
+ }
8491
+
8492
+ // source/gfm-table-lines.ts
8493
+ function findWholeTableStart(lines, from = 0) {
8494
+ for (let i = Math.max(0, from); i + 1 < lines.length; i += 1) if (tableStartsAt(lines, i)) return i;
8495
+ return -1;
8496
+ }
8497
+ function startsLikeTableRow(line) {
8498
+ return /^\s*\|/.test(line);
8499
+ }
8500
+ function isGrowingDelimiterRow(line) {
8501
+ return /^[\s|:-]*-[\s|:-]*$/.test(line);
8502
+ }
8503
+ function trailingTableStart(lines, end = lines.length) {
8504
+ const isTableLine = (line) => startsLikeTableRow(line) || continuesTable(line.trimStart());
8505
+ let start = end;
8506
+ while (start > 0 && isTableLine(lines[start - 1] ?? "")) start -= 1;
8507
+ if (start === end && end > 1 && isGrowingDelimiterRow(lines[end - 1] ?? "") && continuesTable((lines[end - 2] ?? "").trimStart())) {
8508
+ start = end - 1;
8509
+ while (start > 0 && isTableLine(lines[start - 1] ?? "")) start -= 1;
8510
+ }
8511
+ for (let head = start; head < end; head += 1) {
8512
+ if (startsLikeTableRow(lines[head] ?? "")) return head;
8513
+ if (end - head > 1 && isGrowingDelimiterRow(lines[head + 1] ?? "")) return head;
8514
+ }
8515
+ return end;
8516
+ }
8517
+
8090
8518
  exports.ACTION_BY_CLASS = ACTION_BY_CLASS;
8091
8519
  exports.AFTER_WINDOW = AFTER_WINDOW;
8092
8520
  exports.BacktickRuns = BacktickRuns;
@@ -8095,8 +8523,11 @@ exports.CAPABILITY_BY_CLASS = CAPABILITY_BY_CLASS;
8095
8523
  exports.CLASSES = CLASSES;
8096
8524
  exports.CONTROL_KEYS = CONTROL_KEYS;
8097
8525
  exports.CONTROL_KEY_FIELDS = CONTROL_KEY_FIELDS;
8526
+ exports.DIRECTIVE_CONTAINER_OPEN = DIRECTIVE_CONTAINER_OPEN;
8098
8527
  exports.DIRECTIVE_VERSION = DIRECTIVE_VERSION;
8528
+ exports.DirectiveContainerTracker = DirectiveContainerTracker;
8099
8529
  exports.DirectiveDecodeError = DirectiveDecodeError;
8530
+ exports.FENCE_META_KEY = FENCE_META_KEY;
8100
8531
  exports.FENCE_OPENER_WHITESPACE = FENCE_OPENER_WHITESPACE;
8101
8532
  exports.FENCE_WHITESPACE = FENCE_WHITESPACE;
8102
8533
  exports.FenceReader = FenceReader;
@@ -8121,6 +8552,7 @@ exports.RUN_RESULT_KIND = RUN_RESULT_KIND;
8121
8552
  exports.SIDE_EFFECT_CLASSES = SIDE_EFFECT_CLASSES;
8122
8553
  exports.SLUG_PREFIX = SLUG_PREFIX;
8123
8554
  exports.SourceSpliceError = SourceSpliceError;
8555
+ exports.TITLED_IMAGE_LINE = TITLED_IMAGE_LINE;
8124
8556
  exports.XmlContainerTracker = XmlContainerTracker;
8125
8557
  exports.advancePartialKind = advancePartialKind;
8126
8558
  exports.asKindInstance = asKindInstance;
@@ -8135,6 +8567,7 @@ exports.buildKindDirective = buildKindDirective;
8135
8567
  exports.capabilityOf = capabilityOf;
8136
8568
  exports.classifyInboundEnvelopeMetadata = classifyInboundEnvelopeMetadata;
8137
8569
  exports.classifyInnerFenceLine = classifyInnerFenceLine;
8570
+ exports.cleanMarkdown = cleanMarkdown;
8138
8571
  exports.closeFence = closeFence;
8139
8572
  exports.codeSpanText = codeSpanText;
8140
8573
  exports.collectLinkDefinitions = collectLinkDefinitions;
@@ -8165,14 +8598,19 @@ exports.fenceNestsInnerFences = fenceNestsInnerFences;
8165
8598
  exports.fenceOpenerOf = fenceOpenerOf;
8166
8599
  exports.fenceParts = fenceParts;
8167
8600
  exports.fieldsToDbPayload = fieldsToDbPayload;
8601
+ exports.findBalancedXmlClose = findBalancedXmlClose;
8168
8602
  exports.findCodeRanges = findCodeRanges;
8169
8603
  exports.findInlineCodeSpans = findInlineCodeSpans;
8170
8604
  exports.findLinkDefinitionLines = findLinkDefinitionLines;
8171
8605
  exports.findTableEnd = findTableEnd;
8172
8606
  exports.findTableStart = findTableStart;
8607
+ exports.findWholeTableStart = findWholeTableStart;
8173
8608
  exports.fingerprintText = fingerprintText;
8174
8609
  exports.formatBlockLabel = formatBlockLabel;
8175
8610
  exports.getParseSession = getParseSession;
8611
+ exports.hasUnclosedBacktickRun = hasUnclosedBacktickRun;
8612
+ exports.indexOutsideInlineCode = indexOutsideInlineCode;
8613
+ exports.initialXmlBalance = initialXmlBalance;
8176
8614
  exports.injectKindIntoObjectSchema = injectKindIntoObjectSchema;
8177
8615
  exports.irPathIsUnderOrEqual = irPathIsUnderOrEqual;
8178
8616
  exports.irPathKey = irPathKey;
@@ -8184,7 +8622,9 @@ exports.isDuplicateBlockSlug = isDuplicateBlockSlug;
8184
8622
  exports.isEmptyResidue = isEmptyResidue;
8185
8623
  exports.isEscapedBracketMath = isEscapedBracketMath;
8186
8624
  exports.isGfmDelimiterRow = isGfmDelimiterRow;
8625
+ exports.isGrowingDelimiterRow = isGrowingDelimiterRow;
8187
8626
  exports.isHtmlBlockTagName = isHtmlBlockTagName;
8627
+ exports.isInsideInlineCode = isInsideInlineCode;
8188
8628
  exports.isIrEnvelopeCache = isIrEnvelopeCache;
8189
8629
  exports.isJsonAnyField = isJsonAnyField;
8190
8630
  exports.isKindDirective = isKindDirective;
@@ -8216,6 +8656,8 @@ exports.mapCodeRanges = mapCodeRanges;
8216
8656
  exports.mapInlineCodeSpans = mapInlineCodeSpans;
8217
8657
  exports.mapPosition = mapPosition;
8218
8658
  exports.mapRange = mapRange;
8659
+ exports.markdownToPlainText = markdownToPlainText;
8660
+ exports.mathToReadable = mathToReadable;
8219
8661
  exports.mergeResidueIntoValue = mergeResidueIntoValue;
8220
8662
  exports.normalizeAiSchemaInput = normalizeAiSchemaInput;
8221
8663
  exports.normalizeJsonRegion = normalizeJsonRegion;
@@ -8228,7 +8670,9 @@ exports.opensStrippedHtmlBlock = opensStrippedHtmlBlock;
8228
8670
  exports.opensTable = opensTable;
8229
8671
  exports.pairDisplayMath = pairDisplayMath;
8230
8672
  exports.parseDirectiveSlug = parseDirectiveSlug;
8673
+ exports.parseFenceMeta = parseFenceMeta;
8231
8674
  exports.parseFenceOpener = parseFenceOpener;
8675
+ exports.parseLineRanges = parseLineRanges;
8232
8676
  exports.pendingCodeSpanStart = pendingCodeSpanStart;
8233
8677
  exports.pendingDisplayMathOpener = pendingDisplayMathOpener;
8234
8678
  exports.readEnvelope = readEnvelope;
@@ -8241,6 +8685,8 @@ exports.readXmlTag = readXmlTag;
8241
8685
  exports.reconstructRegionValue = reconstructRegionValue;
8242
8686
  exports.rehydrateNodeOutcome = rehydrateNodeOutcome;
8243
8687
  exports.rehydrateRunResult = rehydrateRunResult;
8688
+ exports.removeCodeSpans = removeCodeSpans;
8689
+ exports.replaceFences = replaceFences;
8244
8690
  exports.resetLegacyShellUses = resetLegacyShellUses;
8245
8691
  exports.resolveReferenceLinks = resolveReferenceLinks;
8246
8692
  exports.resolvesInContent = resolvesInContent;
@@ -8256,10 +8702,13 @@ exports.schemaStructureDepth = schemaStructureDepth;
8256
8702
  exports.setJsonRootKeyLookup = setJsonRootKeyLookup;
8257
8703
  exports.singleDollarAfter = singleDollarAfter;
8258
8704
  exports.singleDollarMathEnd = singleDollarMathEnd;
8705
+ exports.soleFence = soleFence;
8259
8706
  exports.spliceSave = spliceSave;
8707
+ exports.splitFenceInfo = splitFenceInfo;
8260
8708
  exports.splitRowSegments = splitRowSegments;
8261
8709
  exports.splitsSurrogatePair = splitsSurrogatePair;
8262
8710
  exports.startsHtmlBlock = startsHtmlBlock;
8711
+ exports.startsLikeTableRow = startsLikeTableRow;
8263
8712
  exports.startsPipelessTable = startsPipelessTable;
8264
8713
  exports.storageToKindSchema = storageToKindSchema;
8265
8714
  exports.stripControlKeys = stripControlKeys;
@@ -8270,10 +8719,13 @@ exports.titleCaseToken = titleCaseToken;
8270
8719
  exports.toCodePointOffset = toCodePointOffset;
8271
8720
  exports.toUtf16Offset = toUtf16Offset;
8272
8721
  exports.tokenizeSource = tokenizeSource;
8722
+ exports.trailingTableStart = trailingTableStart;
8273
8723
  exports.trimFenceLine = trimFenceLine;
8274
8724
  exports.tryDecodeDirective = tryDecodeDirective;
8275
8725
  exports.tryDecodeDirectiveContent = tryDecodeDirectiveContent;
8276
8726
  exports.unescapeCellPipes = unescapeCellPipes;
8727
+ exports.unwrapCodeSpans = unwrapCodeSpans;
8728
+ exports.unwrapKindEnvelopes = unwrapKindEnvelopes;
8277
8729
  exports.validateBlockSchemaSavePlan = validateBlockSchemaSavePlan;
8278
8730
  exports.validateStructuralLeg = validateStructuralLeg;
8279
8731
  exports.withControlKeyFields = withControlKeyFields;