@readme/markdown 14.3.0 → 14.4.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/dist/main.js CHANGED
@@ -11594,6 +11594,7 @@ __webpack_require__.r(__webpack_exports__);
11594
11594
  __webpack_require__.d(__webpack_exports__, {
11595
11595
  Components: () => (/* reexport */ components_namespaceObject),
11596
11596
  FLOW_TYPES: () => (/* reexport */ FLOW_TYPES),
11597
+ INLINE_ONLY_PARENT_TYPES: () => (/* reexport */ INLINE_ONLY_PARENT_TYPES),
11597
11598
  NodeTypes: () => (/* reexport */ NodeTypes),
11598
11599
  Owlmoji: () => (/* reexport */ Owlmoji),
11599
11600
  compile: () => (/* reexport */ lib_compile),
@@ -12208,9 +12209,10 @@ const LightboxPortal = ({ children }) => {
12208
12209
  return (0,external_amd_react_dom_commonjs2_react_dom_commonjs_react_dom_root_ReactDOM_umd_react_dom_.createPortal)(children, document.body);
12209
12210
  };
12210
12211
  const Image = (Props) => {
12211
- const { align = '', alt = '', border: borderProp = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
12212
- // Normalize border: MDXish passes {false} as the string "false", not a boolean
12212
+ const { align = '', alt = '', border: borderProp = false, caption, className = '', framed: framedProp = false, height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
12213
+ // Normalize border/framed: MDXish passes {false} as the string "false", not a boolean
12213
12214
  const border = borderProp === true || borderProp === 'true';
12215
+ const framed = framedProp === true || framedProp === 'true';
12214
12216
  const [lightbox, setLightbox] = external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState(false);
12215
12217
  if (className === 'emoji') {
12216
12218
  return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("img", { alt: alt, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width });
@@ -12239,7 +12241,10 @@ const Image = (Props) => {
12239
12241
  return;
12240
12242
  setLightbox(!lightbox);
12241
12243
  };
12242
- const imgElement = (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("img", { alt: alt, className: `img ${caption || children ? 'img-align-center' : align ? `img-align-${align}` : ''} ${border ? 'border' : ''}`, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width }));
12244
+ // Framed images center the <img> itself; outer wrapper handles left/right alignment via text-align.
12245
+ const imgElement = (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("img", { alt: alt, className: `img ${caption || children || framed ? 'img-align-center' : align ? `img-align-${align}` : ''} ${border ? 'border' : ''}`, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width }));
12246
+ const closedLightbox = (ariaLabel, content) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { "aria-label": ariaLabel, className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
12247
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { className: "lightbox-inner" }, content)));
12243
12248
  const lightboxOverlay = lightbox ? (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement(LightboxPortal, null,
12244
12249
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("div", { className: "markdown-body" },
12245
12250
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { "aria-label": alt || 'Collapse image', className: "img lightbox open", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
@@ -12248,17 +12253,27 @@ const Image = (Props) => {
12248
12253
  (children || caption) && external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figcaption", null, children || caption))),
12249
12254
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("button", { "aria-label": "Minimize image", className: "lightbox-close", onClick: toggle, type: "button" },
12250
12255
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("i", { "aria-hidden": "true", className: "fa-solid fa-xmark" }))))) : null;
12256
+ if (framed) {
12257
+ const frameClass = `img-frame img-frame-${align || 'center'}`;
12258
+ if (children || caption) {
12259
+ return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figure", { className: frameClass },
12260
+ closedLightbox(alt || 'Expand image', imgElement),
12261
+ lightboxOverlay,
12262
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figcaption", null, children || caption)));
12263
+ }
12264
+ return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("div", { className: frameClass },
12265
+ closedLightbox(alt || 'Expand image', imgElement),
12266
+ lightboxOverlay));
12267
+ }
12251
12268
  if (children || caption) {
12252
12269
  return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figure", null,
12253
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { "aria-label": alt, className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
12254
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { className: "lightbox-inner" },
12255
- imgElement,
12256
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figcaption", null, children || caption))),
12270
+ closedLightbox(alt || 'Expand image', external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement(external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.Fragment, null,
12271
+ imgElement,
12272
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figcaption", null, children || caption))),
12257
12273
  lightboxOverlay));
12258
12274
  }
12259
12275
  return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement(external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.Fragment, null,
12260
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { "aria-label": "Expand image", className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
12261
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("span", { className: "lightbox-inner" }, imgElement)),
12276
+ closedLightbox('Expand image', imgElement),
12262
12277
  lightboxOverlay));
12263
12278
  };
12264
12279
  /* harmony default export */ const components_Image = (Image);
@@ -12496,12 +12511,14 @@ const Tab = ({ children }) => {
12496
12511
  };
12497
12512
  const Tabs = ({ children }) => {
12498
12513
  const [activeTab, setActiveTab] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)(0);
12514
+ // React passes `children` as a single element when there's only one child, so normalize.
12515
+ const tabs = external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().Children.toArray(children);
12499
12516
  return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: "TabGroup" },
12500
12517
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("header", null,
12501
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("nav", { className: "TabGroup-nav" }, children?.map((tab, index) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("button", { key: tab.props.title, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
12518
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("nav", { className: "TabGroup-nav" }, tabs.map((tab, index) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("button", { key: tab.props.title, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
12502
12519
  tab.props.icon && (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("i", { className: `TabGroup-icon fa-duotone fa-solid ${tab.props.icon}`, style: { color: `${tab.props.iconColor}` } })),
12503
12520
  tab.props.title))))),
12504
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("section", null, children && children[activeTab])));
12521
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("section", null, tabs[activeTab])));
12505
12522
  };
12506
12523
  /* harmony default export */ const components_Tabs = (Tabs);
12507
12524
 
@@ -13045,6 +13062,34 @@ const FLOW_TYPES = new Set([
13045
13062
  'table',
13046
13063
  'thematicBreak',
13047
13064
  ]);
13065
+ /**
13066
+ * MDAST parent types whose children are restricted to inline content. A block-level
13067
+ * node placed inside any of these violates the parent's content model and breaks
13068
+ * downstream consumers
13069
+ *
13070
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
13071
+ * - paragraph, heading, link, linkReference, emphasis, strong
13072
+ * - tableCell (phrasing minus Break)
13073
+ *
13074
+ * @link {https://github.com/syntax-tree/mdast}
13075
+ *
13076
+ * `delete` is GFM and has *transparent* content, see:
13077
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
13078
+ *
13079
+ * its children must also be phrasing, so it belongs here too.
13080
+ *
13081
+ * NOTE: exported so downstream consumers can reuse and not drift away
13082
+ */
13083
+ const INLINE_ONLY_PARENT_TYPES = new Set([
13084
+ 'paragraph',
13085
+ 'heading',
13086
+ 'tableCell',
13087
+ 'link',
13088
+ 'linkReference',
13089
+ 'emphasis',
13090
+ 'strong',
13091
+ 'delete',
13092
+ ]);
13048
13093
  // HELPER CONSTANTS FOR MDXISH RENDERING
13049
13094
  /**
13050
13095
  * Inline-only custom components that appear as phrasing content within
@@ -75527,7 +75572,15 @@ const walkTags = (html, handlers) => {
75527
75572
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
75528
75573
  const parser = new Parser_Parser({
75529
75574
  onopentag(name) {
75530
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
75575
+ const start = parser.startIndex;
75576
+ const end = tagEnd(parser);
75577
+ handlers.onOpen?.({
75578
+ name,
75579
+ start,
75580
+ end,
75581
+ isSelfClosing: html[end - 2] === '/',
75582
+ isStrayCloser: html[start + 1] === '/',
75583
+ });
75531
75584
  },
75532
75585
  onclosetag(name, implicit) {
75533
75586
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -75582,13 +75635,14 @@ const applyInserts = (html, inserts) => {
75582
75635
  const sorted = [...inserts].sort((a, b) => a.offset - b.offset);
75583
75636
  let out = '';
75584
75637
  let cursor = 0;
75585
- sorted.forEach(({ offset, text }) => {
75638
+ sorted.forEach(({ offset, text, consumes = 0 }) => {
75586
75639
  const clamped = Math.min(Math.max(offset, cursor), html.length);
75587
75640
  if (clamped > cursor) {
75588
75641
  out += html.slice(cursor, clamped);
75589
75642
  cursor = clamped;
75590
75643
  }
75591
75644
  out += text;
75645
+ cursor = Math.min(cursor + consumes, html.length);
75592
75646
  });
75593
75647
  return { value: out + html.slice(cursor), inserts: sorted };
75594
75648
  };
@@ -75700,9 +75754,10 @@ const buildOffsetMapper = (inserts) => {
75700
75754
  // Pre-compute each insert's start in repaired-space.
75701
75755
  let acc = 0;
75702
75756
  const segments = inserts.map(ins => {
75757
+ const consumes = ins.consumes ?? 0;
75703
75758
  const repairedStart = ins.offset + acc;
75704
- acc += ins.text.length;
75705
- return { origOffset: ins.offset, len: ins.text.length, repairedStart };
75759
+ acc += ins.text.length - consumes;
75760
+ return { origOffset: ins.offset, consumes, len: ins.text.length, repairedStart };
75706
75761
  });
75707
75762
  return (repaired) => {
75708
75763
  // Offsets inside an insert's synthetic span have no original counterpart;
@@ -75710,7 +75765,7 @@ const buildOffsetMapper = (inserts) => {
75710
75765
  const hit = segments.find(seg => seg.repairedStart < repaired && repaired < seg.repairedStart + seg.len);
75711
75766
  if (hit)
75712
75767
  return hit.origOffset;
75713
- const shift = segments.reduce((acc2, seg) => (seg.repairedStart < repaired ? acc2 + seg.len : acc2), 0);
75768
+ const shift = segments.reduce((acc2, seg) => (seg.repairedStart < repaired ? acc2 + seg.len - seg.consumes : acc2), 0);
75714
75769
  return repaired - shift;
75715
75770
  };
75716
75771
  };
@@ -75889,6 +75944,48 @@ const HTML_VOID_ELEMENTS = new Set([
75889
75944
 
75890
75945
 
75891
75946
  const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
75947
+ // Intentionally simpler than htmlparser2: `[^>]*` does not honor `>` inside
75948
+ // quoted attribute values. That's acceptable here because the main walker
75949
+ // (htmlparser2) handles attribute parsing; this scan only needs to locate
75950
+ // orphan closers, which can't appear inside an attribute value anyway.
75951
+ const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
75952
+ const findOrphanClosers = (html) => {
75953
+ const masked = maskNonTagRegions(html);
75954
+ const stack = [];
75955
+ const orphans = [];
75956
+ HTML_TAG_TOKEN_RE.lastIndex = 0;
75957
+ let match;
75958
+ while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
75959
+ const name = match[2].toLowerCase();
75960
+ // eslint-disable-next-line no-continue
75961
+ if (!isStandardHtmlTag(name))
75962
+ continue;
75963
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
75964
+ // so we don't need to handle it here.
75965
+ // eslint-disable-next-line no-continue
75966
+ if (name === 'br' && match[1] === '/')
75967
+ continue;
75968
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
75969
+ if (match[1] === '/') {
75970
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
75971
+ // and must be stripped before the strict mdxjs parse runs.
75972
+ if (isVoid) {
75973
+ orphans.push({ offset: match.index, length: match[0].length });
75974
+ // eslint-disable-next-line no-continue
75975
+ continue;
75976
+ }
75977
+ const idx = stack.lastIndexOf(name);
75978
+ if (idx === -1)
75979
+ orphans.push({ offset: match.index, length: match[0].length });
75980
+ else
75981
+ stack.length = idx;
75982
+ }
75983
+ else if (!isVoid && !match[0].endsWith('/>')) {
75984
+ stack.push(name);
75985
+ }
75986
+ }
75987
+ return orphans;
75988
+ };
75892
75989
  /**
75893
75990
  * MDX requires a JSX inline tag and its closer to live on the same line — not
75894
75991
  * just the same paragraph. (`<td>\nArray <object>\n</object></td>` still
@@ -75916,7 +76013,7 @@ const repairUnclosedTags = (html) => {
75916
76013
  const inserts = [];
75917
76014
  const openTags = [];
75918
76015
  walkTags(html, {
75919
- onOpen({ name, start, end }) {
76016
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
75920
76017
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
75921
76018
  // so MDX treats them as literal text instead of expecting a closer
75922
76019
  if (!isStandardHtmlTag(name)) {
@@ -75924,12 +76021,19 @@ const repairUnclosedTags = (html) => {
75924
76021
  return;
75925
76022
  }
75926
76023
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
75927
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
75928
- // If the source open tag doesn't end with `/`, inject one before the
75929
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
75930
- // immediately before `>`.
75931
- if (html[end - 2] !== '/')
76024
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
76025
+ // so we need to rewrite them to self closing tags.
76026
+ if (isStrayCloser) {
76027
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
76028
+ // events <br> per the HTML5 spec. In this case remove the closing /
76029
+ // and fully rewrite the tag to self closing.
76030
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
76031
+ return;
76032
+ }
76033
+ else if (!isSelfClosing) {
76034
+ // Slots in the / right before the closing >
75932
76035
  inserts.push({ offset: end - 1, text: '/' });
76036
+ }
75933
76037
  return;
75934
76038
  }
75935
76039
  openTags.push({ name, start, end });
@@ -75945,6 +76049,7 @@ const repairUnclosedTags = (html) => {
75945
76049
  inserts.push({ offset: findOffsetToPlaceCloser(html, openTag.end, start), text: `</${name}>` });
75946
76050
  },
75947
76051
  });
76052
+ findOrphanClosers(html).forEach(({ offset, length }) => inserts.push({ offset, text: '', consumes: length }));
75948
76053
  return applyInserts(html, inserts);
75949
76054
  };
75950
76055
 
@@ -98374,6 +98479,12 @@ function parseTextChildren(node, processMarkdown, components) {
98374
98479
  node.children = node.children.flatMap(child => {
98375
98480
  if (child.type !== 'text' || !child.value.trim())
98376
98481
  return [child];
98482
+ // Store multiline template-literal children (e.g. {`...\n...`}) as a bare string prop
98483
+ // via node.properties.children so hast-to-hyperscript doesn't wrap them in an array.
98484
+ if (child.value.includes('\n')) {
98485
+ node.properties = { ...node.properties, children: child.value };
98486
+ return [];
98487
+ }
98377
98488
  const hast = processMarkdown(child.value.trim());
98378
98489
  const children = (hast.children ?? []).filter(isElementContentNode);
98379
98490
  // For inline components, preserve plain text instead of wrapping in <p>
@@ -100525,6 +100636,123 @@ function createTokenize(mode) {
100525
100636
  const isSameCaseAsTag = (code) => isLowercaseTag
100526
100637
  ? code >= codes.lowercaseA && code <= codes.lowercaseZ
100527
100638
  : code >= codes.uppercaseA && code <= codes.uppercaseZ;
100639
+ // Shared brace-expression state machine. The two call sites differ only in where
100640
+ // to continue after a line ending and where to return when braceDepth reaches zero.
100641
+ function createBraceExprStates(continuationStart, afterBraceClose) {
100642
+ function braceExpr(code) {
100643
+ if (code === null)
100644
+ return nok(code);
100645
+ if (markdownLineEnding(code)) {
100646
+ if (!isFlow)
100647
+ return nok(code);
100648
+ effects.exit('mdxComponentData');
100649
+ return continuationStart(code);
100650
+ }
100651
+ if (code === codes.quotationMark || code === codes.apostrophe) {
100652
+ quoteChar = code;
100653
+ effects.consume(code);
100654
+ return braceString;
100655
+ }
100656
+ if (code === codes.graveAccent) {
100657
+ inTemplateLit = true;
100658
+ effects.consume(code);
100659
+ return braceTemplateLiteral;
100660
+ }
100661
+ if (code === codes.leftCurlyBrace) {
100662
+ braceDepth += 1;
100663
+ effects.consume(code);
100664
+ return braceExpr;
100665
+ }
100666
+ if (code === codes.rightCurlyBrace) {
100667
+ braceDepth -= 1;
100668
+ effects.consume(code);
100669
+ if (templateStack.length > 0 && braceDepth === templateStack[templateStack.length - 1]) {
100670
+ templateStack.pop();
100671
+ inTemplateLit = true;
100672
+ return braceTemplateLiteral;
100673
+ }
100674
+ if (braceDepth === 0) {
100675
+ return afterBraceClose;
100676
+ }
100677
+ return braceExpr;
100678
+ }
100679
+ effects.consume(code);
100680
+ return braceExpr;
100681
+ }
100682
+ function braceString(code) {
100683
+ if (code === null)
100684
+ return nok(code);
100685
+ if (markdownLineEnding(code)) {
100686
+ if (!isFlow)
100687
+ return nok(code);
100688
+ effects.exit('mdxComponentData');
100689
+ return continuationStart(code);
100690
+ }
100691
+ if (code === codes.backslash) {
100692
+ effects.consume(code);
100693
+ return braceStringEscape;
100694
+ }
100695
+ if (code === quoteChar) {
100696
+ effects.consume(code);
100697
+ quoteChar = null;
100698
+ return braceExpr;
100699
+ }
100700
+ effects.consume(code);
100701
+ return braceString;
100702
+ }
100703
+ function braceStringEscape(code) {
100704
+ if (code === null || markdownLineEnding(code)) {
100705
+ return braceString(code);
100706
+ }
100707
+ effects.consume(code);
100708
+ return braceString;
100709
+ }
100710
+ function braceTemplateLiteral(code) {
100711
+ if (code === null)
100712
+ return nok(code);
100713
+ if (markdownLineEnding(code)) {
100714
+ if (!isFlow)
100715
+ return nok(code);
100716
+ effects.exit('mdxComponentData');
100717
+ return continuationStart(code);
100718
+ }
100719
+ if (code === codes.graveAccent) {
100720
+ inTemplateLit = false;
100721
+ effects.consume(code);
100722
+ return braceExpr;
100723
+ }
100724
+ if (code === codes.backslash) {
100725
+ effects.consume(code);
100726
+ return braceTemplateLiteralEscape;
100727
+ }
100728
+ if (code === codes.dollarSign) {
100729
+ effects.consume(code);
100730
+ return braceTemplateLiteralDollar;
100731
+ }
100732
+ effects.consume(code);
100733
+ return braceTemplateLiteral;
100734
+ }
100735
+ function braceTemplateLiteralEscape(code) {
100736
+ if (code === null || markdownLineEnding(code)) {
100737
+ return braceTemplateLiteral(code);
100738
+ }
100739
+ effects.consume(code);
100740
+ return braceTemplateLiteral;
100741
+ }
100742
+ function braceTemplateLiteralDollar(code) {
100743
+ if (code === codes.leftCurlyBrace) {
100744
+ templateStack.push(braceDepth);
100745
+ braceDepth += 1;
100746
+ inTemplateLit = false;
100747
+ effects.consume(code);
100748
+ return braceExpr;
100749
+ }
100750
+ return braceTemplateLiteral(code);
100751
+ }
100752
+ return { braceExpr, braceString, braceTemplateLiteral };
100753
+ }
100754
+ const { braceExpr: inBraceExpr, braceString: inBraceString, braceTemplateLiteral: inBraceTemplateLiteral, } = createBraceExprStates(openTagContinuationStart, afterOpenTagName);
100755
+ const { braceExpr: inBodyBraceExpr, braceString: inBodyBraceString, braceTemplateLiteral: inBodyBraceTemplateLiteral, } = createBraceExprStates(bodyContinuationStart, body);
100528
100756
  return start;
100529
100757
  // ── Start ──────────────────────────────────────────────────────────────
100530
100758
  function start(code) {
@@ -100647,126 +100875,6 @@ function createTokenize(mode) {
100647
100875
  effects.consume(code);
100648
100876
  return inQuotedAttr;
100649
100877
  }
100650
- function inBraceExpr(code) {
100651
- if (code === null)
100652
- return nok(code);
100653
- if (markdownLineEnding(code)) {
100654
- if (!isFlow)
100655
- return nok(code);
100656
- effects.exit('mdxComponentData');
100657
- return openTagContinuationStart(code);
100658
- }
100659
- // Handle strings inside braces
100660
- if (code === codes.quotationMark || code === codes.apostrophe) {
100661
- quoteChar = code;
100662
- effects.consume(code);
100663
- return inBraceString;
100664
- }
100665
- // Handle template literals inside braces
100666
- if (code === codes.graveAccent) {
100667
- inTemplateLit = true;
100668
- effects.consume(code);
100669
- return inBraceTemplateLiteral;
100670
- }
100671
- if (code === codes.leftCurlyBrace) {
100672
- braceDepth += 1;
100673
- effects.consume(code);
100674
- return inBraceExpr;
100675
- }
100676
- if (code === codes.rightCurlyBrace) {
100677
- braceDepth -= 1;
100678
- effects.consume(code);
100679
- // Check if this } closes a ${...} interpolation
100680
- if (templateStack.length > 0 && braceDepth === templateStack[templateStack.length - 1]) {
100681
- templateStack.pop();
100682
- inTemplateLit = true; // back inside the template literal
100683
- return inBraceTemplateLiteral;
100684
- }
100685
- if (braceDepth === 0) {
100686
- return afterOpenTagName;
100687
- }
100688
- return inBraceExpr;
100689
- }
100690
- effects.consume(code);
100691
- return inBraceExpr;
100692
- }
100693
- function inBraceString(code) {
100694
- if (code === null)
100695
- return nok(code);
100696
- if (markdownLineEnding(code)) {
100697
- if (!isFlow)
100698
- return nok(code);
100699
- effects.exit('mdxComponentData');
100700
- return openTagContinuationStart(code);
100701
- }
100702
- if (code === codes.backslash) {
100703
- effects.consume(code);
100704
- return inBraceStringEscape;
100705
- }
100706
- if (code === quoteChar) {
100707
- effects.consume(code);
100708
- quoteChar = null;
100709
- return inBraceExpr;
100710
- }
100711
- effects.consume(code);
100712
- return inBraceString;
100713
- }
100714
- function inBraceStringEscape(code) {
100715
- if (code === null || markdownLineEnding(code)) {
100716
- return inBraceString(code);
100717
- }
100718
- effects.consume(code);
100719
- return inBraceString;
100720
- }
100721
- // ── Template literal handling inside brace expressions ─────────────────
100722
- function inBraceTemplateLiteral(code) {
100723
- if (code === null)
100724
- return nok(code);
100725
- if (markdownLineEnding(code)) {
100726
- if (!isFlow)
100727
- return nok(code);
100728
- effects.exit('mdxComponentData');
100729
- return openTagContinuationStart(code);
100730
- }
100731
- // Closing backtick ends the template literal
100732
- if (code === codes.graveAccent) {
100733
- inTemplateLit = false;
100734
- effects.consume(code);
100735
- return inBraceExpr;
100736
- }
100737
- // Backslash escape (e.g., \` or \$)
100738
- if (code === codes.backslash) {
100739
- effects.consume(code);
100740
- return inBraceTemplateLiteralEscape;
100741
- }
100742
- // ${ starts an interpolation
100743
- if (code === codes.dollarSign) {
100744
- effects.consume(code);
100745
- return inBraceTemplateLiteralDollar;
100746
- }
100747
- effects.consume(code);
100748
- return inBraceTemplateLiteral;
100749
- }
100750
- function inBraceTemplateLiteralEscape(code) {
100751
- if (code === null || markdownLineEnding(code)) {
100752
- return inBraceTemplateLiteral(code);
100753
- }
100754
- effects.consume(code);
100755
- return inBraceTemplateLiteral;
100756
- }
100757
- function inBraceTemplateLiteralDollar(code) {
100758
- if (code === codes.leftCurlyBrace) {
100759
- // Enter ${...} interpolation. Save current braceDepth so we know
100760
- // when the matching } returns us to this template literal.
100761
- templateStack.push(braceDepth);
100762
- braceDepth += 1;
100763
- inTemplateLit = false; // now inside interpolation expression
100764
- effects.consume(code);
100765
- return inBraceExpr;
100766
- }
100767
- // Just a $ not followed by { — back to template literal
100768
- return inBraceTemplateLiteral(code);
100769
- }
100770
100878
  function selfCloseGt(code) {
100771
100879
  if (code === codes.greaterThan) {
100772
100880
  effects.consume(code);
@@ -100836,6 +100944,15 @@ function createTokenize(mode) {
100836
100944
  codeSpanOpenSize = 0;
100837
100945
  return countOpenTicks(code);
100838
100946
  }
100947
+ // JSX expression child — track braces/template literals so the closing
100948
+ // backtick of `{`...`}` is not misread as a code span opener
100949
+ if (code === codes.leftCurlyBrace) {
100950
+ braceDepth = 1;
100951
+ inTemplateLit = false;
100952
+ effects.consume(code);
100953
+ atLineStart = false;
100954
+ return inBodyBraceExpr;
100955
+ }
100839
100956
  effects.consume(code);
100840
100957
  atLineStart = false;
100841
100958
  return body;
@@ -101076,6 +101193,14 @@ function createTokenize(mode) {
101076
101193
  return bodyContinuationStart(code);
101077
101194
  }
101078
101195
  effects.enter('mdxComponentData');
101196
+ // Resume inside a body brace expression if a line ending interrupted one
101197
+ if (braceDepth > 0) {
101198
+ if (inTemplateLit)
101199
+ return inBodyBraceTemplateLiteral(code);
101200
+ if (quoteChar !== null)
101201
+ return inBodyBraceString(code);
101202
+ return inBodyBraceExpr(code);
101203
+ }
101079
101204
  // Detect tilde fences at line start
101080
101205
  if (atLineStart && code === codes.tilde) {
101081
101206
  return bodyAfterLineStart(code);
@@ -101178,7 +101303,8 @@ const buildInlineMdProcessor = (safeMode) => {
101178
101303
  // Since evaluating expressions can be dangerous, do so only when safeMode is off
101179
101304
  if (!safeMode) {
101180
101305
  const mdxExprExt = mdxExpression({ allowEmpty: true });
101181
- micromarkExts.push({ text: mdxExprExt.text });
101306
+ // We include both flow and text extensions to support both single-line and multi-line expressions
101307
+ micromarkExts.push({ flow: mdxExprExt.flow, text: mdxExprExt.text });
101182
101308
  fromMarkdownExts.push(mdxExpressionFromMarkdown());
101183
101309
  }
101184
101310
  return unified()
@@ -101836,7 +101962,7 @@ const createEvaluatedNode = (result, position) => {
101836
101962
  else if (typeof result === 'object') {
101837
101963
  return { type: 'text', value: JSON.stringify(result), position };
101838
101964
  }
101839
- return { type: 'text', value: String(result).replace(/\s+/g, ' ').trim(), position };
101965
+ return { type: 'text', value: String(result), position };
101840
101966
  };
101841
101967
  /**
101842
101968
  * AST transformer to evaluate MDX expressions.
@@ -103325,6 +103451,7 @@ const mdxishHtmlBlocks = () => tree => {
103325
103451
 
103326
103452
 
103327
103453
 
103454
+
103328
103455
  function toImageAlign(value) {
103329
103456
  if (value === 'left' || value === 'center' || value === 'right') {
103330
103457
  return value;
@@ -103546,7 +103673,7 @@ const transformAnchor = (jsx) => {
103546
103673
  */
103547
103674
  const transformImage = (jsx) => {
103548
103675
  const attrs = getAttrs(jsx);
103549
- const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
103676
+ const { align, alt = '', border, caption, className, framed, height, lazy, src = '', title = '', width } = attrs;
103550
103677
  const validAlign = toImageAlign(align);
103551
103678
  const sizing = width !== undefined ? String(width) : undefined;
103552
103679
  const hProperties = {
@@ -103557,6 +103684,7 @@ const transformImage = (jsx) => {
103557
103684
  ...(border !== undefined && { border: toBool(border) }),
103558
103685
  ...(caption && { caption }),
103559
103686
  ...(className && { className }),
103687
+ ...(framed !== undefined && { framed: toBool(framed) }),
103560
103688
  ...(height !== undefined && { height: String(height) }),
103561
103689
  ...(lazy !== undefined && { lazy: toBool(lazy) }),
103562
103690
  ...(sizing && { sizing }),
@@ -103570,6 +103698,7 @@ const transformImage = (jsx) => {
103570
103698
  caption,
103571
103699
  children: caption ? lib_mdast(caption).children : [],
103572
103700
  className,
103701
+ framed: toBool(framed),
103573
103702
  height: height !== undefined ? String(height) : undefined,
103574
103703
  lazy: toBool(lazy),
103575
103704
  sizing,
@@ -103875,16 +104004,12 @@ const mdxishJsxToMdast = () => tree => {
103875
104004
  parent.children[index] = newNode;
103876
104005
  }
103877
104006
  });
103878
- // Transform magic block images (type: 'image') to image-block
103879
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
104007
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
104008
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
103880
104009
  visit(tree, 'image', (node, index, parent) => {
103881
104010
  if (!parent || index === undefined)
103882
104011
  return SKIP;
103883
- if (parent.type === 'paragraph')
103884
- return SKIP;
103885
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
103886
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
103887
- if (parent.type === 'tableCell')
104012
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
103888
104013
  return SKIP;
103889
104014
  const newNode = transformMagicBlockImage(node);
103890
104015
  parent.children[index] = newNode;
@@ -103938,6 +104063,14 @@ const mdxishJsxToMdast = () => tree => {
103938
104063
  else if (node.border !== undefined) {
103939
104064
  node.border = toBool(node.border);
103940
104065
  }
104066
+ if (hProps.framed !== undefined) {
104067
+ const val = toBool(hProps.framed);
104068
+ node.framed = val;
104069
+ hProps.framed = val;
104070
+ }
104071
+ else if (node.framed !== undefined) {
104072
+ node.framed = toBool(node.framed);
104073
+ }
103941
104074
  // Validate align
103942
104075
  const validAlign = toImageAlign(hProps.align) || toImageAlign(node.align);
103943
104076
  node.align = validAlign;
@@ -104392,11 +104525,30 @@ function normalizeTableSeparator(content) {
104392
104525
 
104393
104526
  ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
104394
104527
 
104528
+
104395
104529
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
104396
104530
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
104531
+ const terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS = ['table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption', 'colgroup'];
104532
+ // Tags whose contents must be preserved as is, inserting a blank line after the
104533
+ // opener corrupts the payload.
104534
+ // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
104535
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS];
104536
+ // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
104537
+ const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
104538
+ open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
104539
+ close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
104540
+ }));
104397
104541
  function isLineHtml(line) {
104398
104542
  return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
104399
104543
  }
104544
+ // True if any RAW_CONTENT_TAGS opener on this line is not closed on the same line.
104545
+ function hasUnclosedRawContentOpener(line) {
104546
+ return RAW_CONTENT_TAG_MATCHERS.some(({ open, close }) => {
104547
+ const opens = (line.match(open) ?? []).length;
104548
+ const closes = (line.match(close) ?? []).length;
104549
+ return opens > closes;
104550
+ });
104551
+ }
104400
104552
  /**
104401
104553
  * Preprocessor to terminate HTML flow blocks.
104402
104554
  *
@@ -104407,15 +104559,16 @@ function isLineHtml(line) {
104407
104559
  *
104408
104560
  * @link https://spec.commonmark.org/0.29/#html-blocks
104409
104561
  *
104410
- * This preprocessor inserts a blank line after standalone HTML lines when the
104411
- * next line is non-blank and not an HTML construct (because they still might be part of the HTML flow),
104412
- * ensuring micromark's HTML flow tokenizer terminates and subsequent content is parsed independently.
104562
+ * This preprocessor inserts a blank line after standalone HTML lines when the next
104563
+ * line is non-blank and not an HTML construct, ensuring micromark's HTML flow
104564
+ * tokenizer terminates and subsequent content is parsed independently.
104413
104565
  *
104414
104566
  * Conditions:
104415
- * 1. Only targets non-indented lines with lowercase tag names. Uppercase tags
104416
- * (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
104417
- * trigger CommonMark HTML blocks, so they are left untouched.
104418
- * 2. Lines inside protected blocks (e.g., code blocks) should be left untouched.
104567
+ * 1. Only non-indented lines with lowercase tag names are considered. Uppercase tags
104568
+ * (e.g. `<Table>`, `<MyComponent>`) are JSX custom components and don't trigger
104569
+ * CommonMark HTML blocks.
104570
+ * 2. Lines inside protected blocks (e.g. fenced code) are left untouched.
104571
+ * 3. Lines with an unclosed RAW_CONTENT_TAGS opener are exempted.
104419
104572
  */
104420
104573
  function terminateHtmlFlowBlocks(content) {
104421
104574
  const { protectedContent, protectedCode } = protectCodeBlocks(content);
@@ -104430,7 +104583,7 @@ function terminateHtmlFlowBlocks(content) {
104430
104583
  }
104431
104584
  const isCurrentLineHtml = isLineHtml(lines[i]);
104432
104585
  const isNextLineHtml = isLineHtml(lines[i + 1]);
104433
- if (isCurrentLineHtml && !isNextLineHtml) {
104586
+ if (isCurrentLineHtml && !isNextLineHtml && !hasUnclosedRawContentOpener(lines[i])) {
104434
104587
  result.push('');
104435
104588
  }
104436
104589
  }
@@ -105248,8 +105401,8 @@ function mdxish(mdContent, opts = {}) {
105248
105401
  const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
105249
105402
  processor
105250
105403
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
105404
+ .use(remarkBreaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
105251
105405
  .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
105252
- .use(remarkBreaks)
105253
105406
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
105254
105407
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
105255
105408
  .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
@@ -105501,7 +105654,10 @@ function exportComponentsForRehype(components) {
105501
105654
  function createElementPreservingHastProps(type, props, ...children) {
105502
105655
  if (props?.node?.properties) {
105503
105656
  const { node, ...rest } = props;
105504
- return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(type, { ...rest, ...node.properties }, ...children);
105657
+ const mergedProps = { ...rest, ...node.properties };
105658
+ // Strip undefined so positional args don't shadow node.properties.children
105659
+ const definedChildren = children.filter(c => c !== undefined);
105660
+ return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(type, mergedProps, ...definedChildren);
105505
105661
  }
105506
105662
  return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(type, props, ...children);
105507
105663
  }