@helping-ai-workflow/md2doc 3.13.0 → 3.13.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.
@@ -47,6 +47,50 @@
47
47
  return out;
48
48
  }
49
49
 
50
+ // v3.13.1 T2 (F-1): text a person typed at the start of a paragraph or
51
+ // list-item line that a renderer would read as a block marker. Measured
52
+ // before this: typing `## `, `- `, `> ` or `1. ` at a paragraph start saved
53
+ // the line unescaped, and the next render silently turned the paragraph
54
+ // into a heading, list or quote.
55
+ //
56
+ // Input is escapeText() output, possibly several lines (a hard break's
57
+ // backslash-newline, or a lazy continuation). Each line, after up to three
58
+ // spaces of indent, gets the FIRST matching rule below; nothing else is
59
+ // touched. The list is the decision:
60
+ // ATX heading `#`..`######` then space/tab/end -> `\#…`
61
+ // bullet `-` or `+` then space/tab/end -> `\-…` / `\+…`
62
+ // blockquote `>` (no space needed) -> `\>…`
63
+ // ordered 1-9 digits, `.` or `)`, space/end -> `1\.` / `1\)`
64
+ // setext/rule a run of `=` or of `-`, alone -> `\===` / `\---`
65
+ // spaced rule 3+ `-` with spaces between, alone -> `\-- -`
66
+ // table row `|` -> `\|…`
67
+ // `*`, `_`, backtick, `~`, `<` and `[` never get here unescaped
68
+ // (escapeText()). A line whose first character is `\` matches nothing,
69
+ // which is what keeps already-escaped text byte-stable. `-5 dBm` and
70
+ // `1.5 V` match nothing either: the marker must be followed by a space, a
71
+ // tab or the end of the line. Indented code (4+ spaces) is out of scope.
72
+ const BLOCK_START_RULES = [
73
+ { re: /^#{1,6}(?=[ \t]|$)/, fix: (rest) => '\\' + rest },
74
+ { re: /^[-+](?=[ \t]|$)/, fix: (rest) => '\\' + rest },
75
+ { re: /^>/, fix: (rest) => '\\' + rest },
76
+ { re: /^(\d{1,9})([.)])(?=[ \t]|$)/, fix: (rest) => rest.replace(/^(\d{1,9})([.)])/, '$1\\$2') },
77
+ { re: /^(=+|-+)[ \t]*$/, fix: (rest) => '\\' + rest },
78
+ // A thematic break may have spaces between its dashes (`-- -`,
79
+ // `---- -`); the run rule above only sees an unbroken one.
80
+ { re: /^-(?:[ \t]*-){2,}[ \t]*$/, fix: (rest) => '\\' + rest },
81
+ { re: /^\|/, fix: (rest) => '\\' + rest },
82
+ ];
83
+ function escapeBlockStarts(md) {
84
+ return String(md).split('\n').map((line) => {
85
+ const ind = /^ {0,3}/.exec(line)[0];
86
+ const rest = line.slice(ind.length);
87
+ for (let i = 0; i < BLOCK_START_RULES.length; i++) {
88
+ if (BLOCK_START_RULES[i].re.test(rest)) return ind + BLOCK_START_RULES[i].fix(rest);
89
+ }
90
+ return line;
91
+ }).join('\n');
92
+ }
93
+
50
94
  // Real contenteditable output wraps plain runs in attribute-less <span>s
51
95
  // (Chrome/Firefox formatting artifacts). Those are transparent. A <span>
52
96
  // carrying any of these attributes is a real style/class/behavior carrier
@@ -358,5 +402,5 @@
358
402
  return serializeInline(rootEl).unsupported.length === 0;
359
403
  }
360
404
 
361
- return { serializeInline, canWysiwyg, escapeText };
405
+ return { serializeInline, canWysiwyg, escapeText, escapeBlockStarts };
362
406
  });
@@ -224,7 +224,9 @@
224
224
 
225
225
  // strip leading whitespace left behind by a dropped leading element
226
226
  // (e.g. a checkbox <input>) — see header note.
227
- const itemMd = rawItemMd.replace(/^[ \t]+/, '');
227
+ // v3.13.1 T2 (F-1): text that begins with a block marker is escaped,
228
+ // or the item's own text would re-lex as a nested heading/list/quote.
229
+ const itemMd = inlineMd.escapeBlockStarts(rawItemMd.replace(/^[ \t]+/, ''));
228
230
 
229
231
  // Two-part marker: bullet (ordered ordinal or plain '- ') and an
230
232
  // optional checkbox prefix. The two parts are independent so that
@@ -617,6 +619,11 @@
617
619
  // zero-width character it never asked for, and the item would stop
618
620
  // reading as empty to every `itemMd === ''` test in this function.
619
621
  if (SETEXT_ESCAPE_RE.test(itemMd)) itemMd = '';
622
+ // v3.13.1 T2 (F-1): an item whose text begins with `## `, `- `, `> `,
623
+ // `1. ` (…) is written escaped, or the next render reads it as a
624
+ // nested block. Only re-serialized items get here; a bystander
625
+ // replayed from its source lines never does.
626
+ itemMd = inlineMd.escapeBlockStarts(itemMd);
620
627
  }
621
628
 
622
629
  // Anything in the block that is neither chrome nor the text surface is