@ai-react-markdown/engine 2.4.2 → 2.4.5

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.
@@ -269,6 +269,11 @@ var defaultUrlTransform = (value) => {
269
269
  var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
270
270
  var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
271
271
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
272
+ var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
273
+ var TYPE1_NAMES = /* @__PURE__ */ new Set(["script", "pre", "style", "textarea"]);
274
+ var TYPE6_START_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r]|\/?>|$)/;
275
+ var TYPE1_START_RE = /^<(script|pre|style|textarea)(?:[ \t\r]|>|$)/i;
276
+ var TYPE7_LINE_RE = /^<(\/?)([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r][^>]*|\/)?>[ \t\r]*$/;
272
277
  var VOID_TAGS = /* @__PURE__ */ new Set([
273
278
  "area",
274
279
  "base",
@@ -407,7 +412,11 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
407
412
  htmlFlowSinceBlank: false,
408
413
  htmlSeamPending: false,
409
414
  phasePoisonedAt: Infinity,
410
- pendingTruncatedTags: []
415
+ pendingTruncatedTags: [],
416
+ pendingTruncatedCloses: [],
417
+ tagAcrossLines: false,
418
+ tagAcrossLinesIndent: 0,
419
+ htmlFlowReal: false
411
420
  };
412
421
  }
413
422
  function isPlausibleLinkDefRest(rest) {
@@ -704,6 +713,8 @@ function processConfirmedLine(cp, ln, text) {
704
713
  for (const tag of cp.pendingTruncatedTags) applyTag(tag, true);
705
714
  cp.pendingTruncatedTags = [];
706
715
  }
716
+ cp.pendingTruncatedCloses = [];
717
+ cp.tagAcrossLines = false;
707
718
  cp.blankRun += 1;
708
719
  cp.lastBlankStart = ln.start;
709
720
  cp.candidates.push({
@@ -717,6 +728,7 @@ function processConfirmedLine(cp, ln, text) {
717
728
  cp.paragraphHasUnpairedRun = false;
718
729
  cp.openBracket = null;
719
730
  cp.htmlFlowSinceBlank = false;
731
+ cp.htmlFlowReal = false;
720
732
  cp.prevLineBlank = true;
721
733
  cp.prevLineWasText = false;
722
734
  cp.prevLineWasValidDef = false;
@@ -732,6 +744,16 @@ function processConfirmedLine(cp, ln, text) {
732
744
  if (tagStart) {
733
745
  cp.htmlFlowSinceBlank = true;
734
746
  if (!TYPE6_NAMES.has(tagStart[1].toLowerCase())) cp.hazardVerdict = true;
747
+ if (!cp.htmlFlowReal) {
748
+ const t = mdTrimStart(ln.text);
749
+ const t6 = TYPE6_START_RE.exec(t);
750
+ const t7 = TYPE7_LINE_RE.exec(t);
751
+ if (t6 !== null && TYPE6_NAMES.has(t6[1].toLowerCase()) || TYPE1_START_RE.test(t) || // Type 7 cannot interrupt a paragraph, and excludes the raw-text
752
+ // names (those are type 1 as start tags, paragraph as end tags).
753
+ t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7[2].toLowerCase())) {
754
+ cp.htmlFlowReal = true;
755
+ }
756
+ }
735
757
  }
736
758
  const inRawText = cp.htmlFlowSinceBlank || rawOpenAtLineStart;
737
759
  const rawFlowStart = ln.indent <= 3 && /^<(?:!--|\?|![A-Za-z]|!\[CDATA\[)/.test(mdTrimStart(ln.text));
@@ -872,7 +894,21 @@ ${cont(scanText)}` };
872
894
  for (const [from, to] of rawSpans) {
873
895
  tagText = tagText.slice(0, from) + " ".repeat(to - from) + tagText.slice(to);
874
896
  }
875
- {
897
+ let skipTagScan = false;
898
+ if (cp.tagAcrossLines) {
899
+ if (ln.indent < cp.tagAcrossLinesIndent) poisonRawDivergence();
900
+ const gt = ln.text.indexOf(">");
901
+ if (gt === -1) {
902
+ skipTagScan = true;
903
+ } else {
904
+ if (/["']/.test(ln.text.slice(0, gt))) poisonRawDivergence();
905
+ for (const tag of cp.pendingTruncatedCloses) applyTag(tag, true);
906
+ cp.pendingTruncatedCloses = [];
907
+ cp.tagAcrossLines = false;
908
+ tagText = " ".repeat(gt + 1) + tagText.slice(gt + 1);
909
+ }
910
+ }
911
+ if (!skipTagScan) {
876
912
  TAG_OR_COMMENT_RE.lastIndex = 0;
877
913
  let m;
878
914
  let lastCommentOpenerIdx = -1;
@@ -902,6 +938,7 @@ ${cont(scanText)}` };
902
938
  if (cp.commentOpen) continue;
903
939
  const closing = m[1] === "/";
904
940
  const tag = m[2].toLowerCase();
941
+ if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + m.index);
905
942
  const selfClosing = m[3] !== void 0 && /\/\s*$/.test(m[3]);
906
943
  if (VOID_TAGS.has(tag) || selfClosing) continue;
907
944
  applyTag(tag, closing);
@@ -922,6 +959,7 @@ ${cont(scanText)}` };
922
959
  if (startMasked || wholeVisible || inRaw(mr.index) || cp.commentOpen) continue;
923
960
  const closing = mr[1] === "/";
924
961
  const tag = mr[2].toLowerCase();
962
+ if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + mr.index);
925
963
  const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
926
964
  if (VOID_TAGS.has(tag) || selfClosing) continue;
927
965
  applyTag(tag, closing);
@@ -937,7 +975,14 @@ ${cont(scanText)}` };
937
975
  if (m2) {
938
976
  const closing = m2[1] === "/";
939
977
  const tag = m2[2].toLowerCase();
940
- if (!VOID_TAGS.has(tag)) {
978
+ if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
979
+ if (cp.htmlFlowReal) {
980
+ cp.tagAcrossLines = true;
981
+ cp.tagAcrossLinesIndent = ln.indent;
982
+ }
983
+ if (closing) {
984
+ if (!VOID_TAGS.has(tag) && cp.htmlFlowReal) cp.pendingTruncatedCloses.push(tag);
985
+ } else if (!VOID_TAGS.has(tag)) {
941
986
  applyTag(tag, closing);
942
987
  const rawLastLt = ln.text.lastIndexOf("<");
943
988
  const rawTruncated = rawLastLt !== -1 && !ln.text.includes(">", rawLastLt);
@@ -1149,6 +1194,8 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1149
1194
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1150
1195
  }
1151
1196
  }
1197
+ var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1198
+ var STRAY_SYNTHESIZED_END_TAG_RE = /<\/(?:br|p)\b/i;
1152
1199
  function spliceTrees(input) {
1153
1200
  const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1154
1201
  const injectedLen = injectionPrefix.length;
@@ -1205,6 +1252,12 @@ function spliceTrees(input) {
1205
1252
  return !(start !== void 0 && start < injectedLen);
1206
1253
  });
1207
1254
  const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1255
+ if (prefixMdast.some((c) => c.type === "html" && TABLE_PART_TAG_RE.test(c.value))) return null;
1256
+ for (const child of tailMdastChildren) {
1257
+ if (isWrapInvisible(child)) continue;
1258
+ if (child.type !== "html") break;
1259
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1260
+ }
1208
1261
  const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1209
1262
  if (aligned === null) return null;
1210
1263
  const hastChildren = aligned.children;
@@ -1330,6 +1383,9 @@ function alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible) {
1330
1383
  return null;
1331
1384
  }
1332
1385
  const lastIsLiteral = last !== void 0 && last.type === "text" && last.value.trim() !== "";
1386
+ if (lastIsLiteral && pairIdx >= 0 && visibles[pairIdx].type !== "html") {
1387
+ return null;
1388
+ }
1333
1389
  const litOwnerEnd = pairIdx >= 0 ? visibles[pairIdx].position?.end?.offset : void 0;
1334
1390
  const litEnd = lastIsLiteral ? last.position?.end?.offset : void 0;
1335
1391
  if (lastIsLiteral && last.position !== void 0 && (litEnd === void 0 || litOwnerEnd === void 0)) {
@@ -1418,8 +1474,9 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
1418
1474
  }
1419
1475
  function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1420
1476
  const firstText = tailHastChildren[0];
1421
- if (!firstText) return false;
1422
1477
  const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1478
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1479
+ if (!firstText) return false;
1423
1480
  if (!isSeparatorText(firstText)) {
1424
1481
  if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1425
1482
  if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
@@ -1468,6 +1525,9 @@ function countTrailingNewlines(value) {
1468
1525
  function countNewlines(text, end = text.length) {
1469
1526
  let count = 0;
1470
1527
  for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1528
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1529
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1530
+ }
1471
1531
  return count;
1472
1532
  }
1473
1533
 
@@ -1587,7 +1647,7 @@ function normalizeId(s) {
1587
1647
  return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
1588
1648
  }
1589
1649
  function normalizeForMatch(s) {
1590
- return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\(.)/g, "$1"));
1650
+ return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\([!-/:-@[-`{-~])/g, "$1"));
1591
1651
  }
1592
1652
 
1593
1653
  // src/components/collectDefLabels.ts
@@ -1860,17 +1920,8 @@ function phantomSuffixCloser(content) {
1860
1920
  }
1861
1921
 
1862
1922
  // src/components/extractContributions.ts
1863
- function fakeAnchorElement(url) {
1864
- return { type: "element", tagName: "a", properties: { href: url }, children: [] };
1865
- }
1866
- function sanitizeDefUrl(url, urlTransform) {
1867
- if (!urlTransform) return url;
1868
- const result = urlTransform(url, "href", fakeAnchorElement(url));
1869
- return result == null ? "" : String(result);
1870
- }
1871
1923
  function* extractContributions(mdast, options = {}) {
1872
1924
  const phantomFn = options.phantomFootnoteLabels;
1873
- const urlTransform = options.urlTransform;
1874
1925
  const out = [];
1875
1926
  (0, import_unist_util_visit3.visit)(mdast, (n) => {
1876
1927
  if (n.type === "footnoteReference") {
@@ -1898,7 +1949,7 @@ function* extractContributions(mdast, options = {}) {
1898
1949
  out.push({
1899
1950
  kind: "linkDef",
1900
1951
  label: normalizeId(d.identifier),
1901
- url: sanitizeDefUrl(d.url, urlTransform),
1952
+ url: d.url,
1902
1953
  title: d.title
1903
1954
  });
1904
1955
  }
@@ -3612,8 +3663,7 @@ function isProtocolAllowed(url, allowed) {
3612
3663
  }
3613
3664
  function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
3614
3665
  rawUrl = (0, import_micromark_util_sanitize_uri2.normalizeUri)(rawUrl);
3615
- const callerProtocols = schema.protocols;
3616
- const allowed = callerProtocols === void 0 || callerProtocols === null ? sanitizeSchema.protocols?.[key] : callerProtocols[key];
3666
+ const allowed = Object.hasOwn(schema, "protocols") ? schema.protocols?.[key] : sanitizeSchema.protocols?.[key];
3617
3667
  if (allowed && allowed.length > 0 && !isProtocolAllowed(rawUrl, allowed)) return null;
3618
3668
  const transformed = urlTransform(rawUrl, key, fakeElement(tagName, key, rawUrl));
3619
3669
  if (transformed == null) return null;
@@ -4023,23 +4073,28 @@ function lineHasBacktick(content, pos) {
4023
4073
  }
4024
4074
  return false;
4025
4075
  }
4026
- function isAtLineStart(content, pos) {
4076
+ function lineIndentBefore(content, pos) {
4027
4077
  let i = pos - 1;
4028
- let spaces = 0;
4029
- while (i >= 0 && content[i] === " ") {
4030
- spaces++;
4031
- if (spaces > 3) return false;
4078
+ let indent = 0;
4079
+ while (i >= 0 && (content[i] === " " || content[i] === " ")) {
4080
+ indent += content[i] === " " ? 4 : 1;
4032
4081
  i--;
4033
4082
  }
4034
- return i < 0 || content[i] === "\n" || content[i] === "\r";
4083
+ return i < 0 || content[i] === "\n" || content[i] === "\r" ? indent : -1;
4035
4084
  }
4036
4085
  function findClosingBacktickRun(content, start, n) {
4037
4086
  let i = start;
4038
4087
  while (i < content.length) {
4039
- if (content[i] === "`") {
4088
+ const ch = content[i];
4089
+ if (ch === "`") {
4040
4090
  const runLen = getRepeatedMarkerLength(content, i, "`");
4041
4091
  if (runLen === n) return i;
4042
4092
  i += runLen;
4093
+ } else if (ch === "\n") {
4094
+ let j = i + 1;
4095
+ while (j < content.length && (content[j] === " " || content[j] === " " || content[j] === "\r")) j += 1;
4096
+ if (j >= content.length || content[j] === "\n") return -1;
4097
+ i += 1;
4043
4098
  } else {
4044
4099
  i += 1;
4045
4100
  }
@@ -4052,6 +4107,7 @@ function splitByProtectedRegions(content) {
4052
4107
  let multilineStart = -1;
4053
4108
  let multilineFenceMarker = null;
4054
4109
  let multilineFenceLength = 0;
4110
+ let multilineFenceIndent = 0;
4055
4111
  function pushProtected(start, end) {
4056
4112
  if (start > lastIndex) {
4057
4113
  segments.push({ text: content.substring(lastIndex, start), isCode: false });
@@ -4065,7 +4121,8 @@ function splitByProtectedRegions(content) {
4065
4121
  if (multilineStart !== -1) {
4066
4122
  if (char === multilineFenceMarker) {
4067
4123
  const runLen = getRepeatedMarkerLength(content, i, multilineFenceMarker);
4068
- if (runLen >= multilineFenceLength && isAtLineStart(content, i) && restOfLineIsBlank(content, i + runLen)) {
4124
+ const closerIndent = lineIndentBefore(content, i);
4125
+ if (runLen >= multilineFenceLength && closerIndent !== -1 && closerIndent <= multilineFenceIndent + 3 && restOfLineIsBlank(content, i + runLen)) {
4069
4126
  pushProtected(multilineStart, i + runLen);
4070
4127
  multilineStart = -1;
4071
4128
  multilineFenceMarker = null;
@@ -4081,10 +4138,12 @@ function splitByProtectedRegions(content) {
4081
4138
  }
4082
4139
  if (char === "`" || char === "~") {
4083
4140
  const runLen = getRepeatedMarkerLength(content, i, char);
4084
- if (runLen >= 3 && isAtLineStart(content, i) && !(char === "`" && lineHasBacktick(content, i + runLen))) {
4141
+ const openerIndent = lineIndentBefore(content, i);
4142
+ if (runLen >= 3 && openerIndent !== -1 && !(char === "`" && lineHasBacktick(content, i + runLen))) {
4085
4143
  multilineStart = i;
4086
4144
  multilineFenceMarker = char;
4087
4145
  multilineFenceLength = runLen;
4146
+ multilineFenceIndent = openerIndent;
4088
4147
  i += runLen;
4089
4148
  continue;
4090
4149
  }
@@ -4157,16 +4216,20 @@ function escapeCurrencyDollarSigns(text) {
4157
4216
  currentLineProcessed += segment;
4158
4217
  }
4159
4218
  let needEscape = true;
4160
- let restBeforeNextMatchOrEnd = "";
4161
- if (i < currencyMatches.length - 1) {
4162
- const nextMatch = currencyMatches[i + 1];
4163
- if (nextMatch.index - match.index > 1) {
4164
- restBeforeNextMatchOrEnd = text.substring(match.index + 1, nextMatch.index);
4219
+ const restStart = match.index + 1;
4220
+ const restEnd = i < currencyMatches.length - 1 ? currencyMatches[i + 1].index : text.length;
4221
+ let firstLineBeforeNextMatch = "";
4222
+ if (restEnd - restStart > 0) {
4223
+ let eol = restEnd;
4224
+ for (let k = restStart; k < restEnd; k++) {
4225
+ const c = text.charCodeAt(k);
4226
+ if (c === 10 || c === 13) {
4227
+ eol = k;
4228
+ break;
4229
+ }
4165
4230
  }
4166
- } else {
4167
- restBeforeNextMatchOrEnd = text.substring(match.index + 1);
4231
+ firstLineBeforeNextMatch = text.substring(restStart, eol);
4168
4232
  }
4169
- const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\r\n|\r|\n/g)[0];
4170
4233
  if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
4171
4234
  const wholeLineBeforeNextMatchWithoutCurrentDollar = currentLineProcessed + firstLineBeforeNextMatch;
4172
4235
  if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
@@ -4332,39 +4395,49 @@ function hasUnclosedTextCommand(text) {
4332
4395
  return false;
4333
4396
  }
4334
4397
  var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
4335
- function processSliceInstrumented(slice) {
4398
+ var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
4399
+ function processSliceInstrumented(slice, probe = true) {
4336
4400
  const segments = splitByProtectedRegions(slice);
4337
- let out = "";
4401
+ const parts = [];
4338
4402
  let quiescent = true;
4339
4403
  let truncatedAtSeamStart = false;
4340
4404
  for (let index = 0; index < segments.length; index++) {
4341
4405
  const segment = segments[index];
4342
4406
  if (segment.isCode) {
4343
- out += segment.text;
4407
+ parts.push(segment.text);
4344
4408
  continue;
4345
4409
  }
4346
4410
  let text = segment.text;
4347
4411
  text = escapeMhchemCommands(text);
4348
4412
  text = escapeCurrencyDollarSigns(text);
4349
4413
  text = convertLatexDelimiters(text);
4350
- if (RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
4414
+ if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
4351
4415
  text = escapeLatexPipes(text);
4352
- if (findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
4416
+ if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
4353
4417
  text = escapeLatexPipesInUnclosed(text);
4354
- if (hasUnclosedTextCommand(text)) quiescent = false;
4418
+ if (probe && hasUnclosedTextCommand(text)) quiescent = false;
4355
4419
  text = escapeTextUnderscores(text);
4356
4420
  text = convertSingleToDoubleDollar(text);
4357
- const unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4358
- if (unclosedDouble !== -1) {
4359
- quiescent = false;
4360
- if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
4361
- truncatedAtSeamStart = true;
4421
+ if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
4422
+ const unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4423
+ if (unclosedDouble !== -1) {
4424
+ quiescent = false;
4425
+ if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
4426
+ truncatedAtSeamStart = true;
4427
+ }
4362
4428
  }
4363
4429
  }
4364
4430
  text = truncateUnclosedLatexBlock(text);
4365
- out += text;
4431
+ parts.push(text);
4366
4432
  }
4367
- return { out, quiescent, truncatedAtSeamStart };
4433
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart };
4434
+ }
4435
+ function isBlankRawLine(text, from, to) {
4436
+ for (let i = from; i < to; i++) {
4437
+ const c = text.charCodeAt(i);
4438
+ if (c !== 32 && c !== 9 && c !== 13) return false;
4439
+ }
4440
+ return true;
4368
4441
  }
4369
4442
  function findRawSafeCut(active) {
4370
4443
  const segments = splitByProtectedRegions(active);
@@ -4379,10 +4452,12 @@ function findRawSafeCut(active) {
4379
4452
  continue;
4380
4453
  }
4381
4454
  const text = segment.text;
4455
+ let atLineStart = offset === 0 || active.charCodeAt(offset - 1) === 10;
4382
4456
  let lineStart = 0;
4383
4457
  while (lineStart <= text.length) {
4384
4458
  const nl = text.indexOf("\n", lineStart);
4385
4459
  const lineEnd = nl === -1 ? text.length : nl;
4460
+ if (atLineStart && nl !== -1 && isBlankRawLine(text, lineStart, lineEnd)) backtickHazard = false;
4386
4461
  for (let i = lineStart; i < lineEnd; i++) {
4387
4462
  const ch = text[i];
4388
4463
  if (ch === "`") backtickHazard = true;
@@ -4395,6 +4470,7 @@ function findRawSafeCut(active) {
4395
4470
  if (nl === -1) break;
4396
4471
  if (!backtickHazard && !latentLt) lastCut = offset + nl + 1;
4397
4472
  lineStart = nl + 1;
4473
+ atLineStart = true;
4398
4474
  }
4399
4475
  offset += text.length;
4400
4476
  }
@@ -4403,11 +4479,14 @@ function findRawSafeCut(active) {
4403
4479
  var DEFAULT_FREEZE_ATTEMPT_THRESHOLD = 512;
4404
4480
  function createIncrementalLatexPreprocessor(options) {
4405
4481
  const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
4482
+ const onAttempt = options?.onAttempt;
4483
+ const backoff = options?.backoff ?? true;
4406
4484
  let prevSource = "";
4407
4485
  let prevOutput = "";
4408
4486
  let frozenSrcEnd = 0;
4409
4487
  let frozenOut = "";
4410
4488
  let triggered = false;
4489
+ let nextAttemptLen = 0;
4411
4490
  return function incrementalPreprocessLaTeX(source) {
4412
4491
  if (source === prevSource) return prevOutput;
4413
4492
  const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
@@ -4415,6 +4494,7 @@ function createIncrementalLatexPreprocessor(options) {
4415
4494
  frozenSrcEnd = 0;
4416
4495
  frozenOut = "";
4417
4496
  triggered = false;
4497
+ nextAttemptLen = 0;
4418
4498
  }
4419
4499
  if (!triggered) {
4420
4500
  const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
@@ -4426,18 +4506,26 @@ function createIncrementalLatexPreprocessor(options) {
4426
4506
  triggered = true;
4427
4507
  }
4428
4508
  let active = source.slice(frozenSrcEnd);
4429
- if (active.length > freezeThreshold) {
4509
+ if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
4510
+ const activeLength = active.length;
4511
+ let advanced = false;
4512
+ let frozenBytes = 0;
4513
+ const freeze = (cut2, slice) => {
4514
+ frozenOut += slice.out;
4515
+ frozenSrcEnd += cut2;
4516
+ active = source.slice(frozenSrcEnd);
4517
+ advanced = true;
4518
+ frozenBytes = cut2;
4519
+ };
4430
4520
  const cut = findRawSafeCut(active);
4431
4521
  if (cut > 0) {
4432
4522
  const candidate = processSliceInstrumented(active.slice(0, cut));
4433
- if (candidate.quiescent) {
4434
- frozenOut += candidate.out;
4435
- frozenSrcEnd += cut;
4436
- active = source.slice(frozenSrcEnd);
4437
- }
4523
+ if (candidate.quiescent) freeze(cut, candidate);
4438
4524
  }
4525
+ nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
4526
+ onAttempt?.({ activeLength, frozenBytes });
4439
4527
  }
4440
- const tail = processSliceInstrumented(active);
4528
+ const tail = processSliceInstrumented(active, false);
4441
4529
  const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
4442
4530
  const out = head + tail.out;
4443
4531
  prevSource = source;