@readme/markdown 13.7.2 → 13.7.4

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.node.js CHANGED
@@ -19358,10 +19358,47 @@ var dist_utils_default = /*#__PURE__*/__webpack_require__.n(dist_utils);
19358
19358
 
19359
19359
  let mermaid;
19360
19360
  const { uppercase } = (dist_utils_default());
19361
+ // Module-level queue that batches mermaid nodes across all CodeTabs instances into a
19362
+ // single mermaid.run() call. This is necessary because mermaid generates SVG element IDs
19363
+ // using Date.now(), which collides when multiple diagrams render in the same millisecond.
19364
+ // Colliding IDs cause diagrams to overlap or break layout.
19365
+ //
19366
+ // Why not use `deterministicIDSeed` with a unique ID per diagram? Mermaid's implementation
19367
+ // only uses seed.length (not the seed value) to compute the starting ID, so every UUID
19368
+ // (36 chars) produces the same `mermaid-36` prefix — the collision remains.
19369
+ // See: https://github.com/mermaid-js/mermaid/blob/mermaid%4011.12.0/packages/mermaid/src/utils.ts#L755-L761
19370
+ //
19371
+ // These vars must be module-scoped (not per-instance refs) because the batching requires
19372
+ // cross-instance coordination. They are short-lived: the queue drains on the next macrotask
19373
+ // and cleanup clears everything on unmount.
19374
+ let mermaidQueue = [];
19375
+ let mermaidFlushTimer = null;
19376
+ let currentTheme;
19377
+ function queueMermaidNode(node, theme) {
19378
+ mermaidQueue.push(node);
19379
+ currentTheme = theme;
19380
+ if (!mermaidFlushTimer) {
19381
+ // setTimeout(0) defers to a macrotask, after all useEffects have queued their nodes
19382
+ mermaidFlushTimer = setTimeout(async () => {
19383
+ const nodes = [...mermaidQueue];
19384
+ mermaidQueue = [];
19385
+ mermaidFlushTimer = null;
19386
+ const module = await __webpack_require__.e(/* import() */ 486).then(__webpack_require__.bind(__webpack_require__, 5486));
19387
+ mermaid = module.default;
19388
+ mermaid.initialize({
19389
+ startOnLoad: false,
19390
+ theme: currentTheme === 'dark' ? 'dark' : 'default',
19391
+ deterministicIds: true,
19392
+ });
19393
+ await mermaid.run({ nodes });
19394
+ }, 0);
19395
+ }
19396
+ }
19361
19397
  const CodeTabs = (props) => {
19362
19398
  const { children } = props;
19363
19399
  const theme = (0,external_react_.useContext)(Theme);
19364
19400
  const isHydrated = useHydrated();
19401
+ const mermaidRef = (0,external_react_.useRef)(null);
19365
19402
  // Handle both array (from rehype-react in rendering mdxish) and single element (MDX/JSX runtime) cases
19366
19403
  // The children here is the individual code block objects
19367
19404
  const childrenArray = Array.isArray(children) ? children : [children];
@@ -19372,22 +19409,20 @@ const CodeTabs = (props) => {
19372
19409
  return Array.isArray(pre?.props?.children) ? pre.props.children[0] : pre?.props?.children;
19373
19410
  };
19374
19411
  const containAtLeastOneMermaid = childrenArray.some(pre => getCodeComponent(pre)?.props?.lang === 'mermaid');
19375
- // Render Mermaid diagram
19376
19412
  (0,external_react_.useEffect)(() => {
19377
- // Ensure we only render mermaids when frontend is hydrated to avoid hydration errors
19378
- // because mermaid mutates the DOM before react hydrates
19379
- if (typeof window !== 'undefined' && containAtLeastOneMermaid && isHydrated) {
19380
- __webpack_require__.e(/* import() */ 486).then(__webpack_require__.bind(__webpack_require__, 5486)).then(module => {
19381
- mermaid = module.default;
19382
- mermaid.initialize({
19383
- startOnLoad: false,
19384
- theme: theme === 'dark' ? 'dark' : 'default',
19385
- });
19386
- mermaid.run({
19387
- nodes: document.querySelectorAll('.mermaid-render'),
19388
- });
19389
- });
19390
- }
19413
+ // Wait for hydration so mermaid's DOM mutations don't cause mismatches
19414
+ if (typeof window !== 'undefined' && containAtLeastOneMermaid && isHydrated && mermaidRef.current) {
19415
+ queueMermaidNode(mermaidRef.current, theme);
19416
+ }
19417
+ return () => {
19418
+ // Clear the batch timer on unmount to prevent mermaid from running
19419
+ // after the DOM is torn down
19420
+ if (mermaidFlushTimer) {
19421
+ clearTimeout(mermaidFlushTimer);
19422
+ mermaidFlushTimer = null;
19423
+ mermaidQueue = [];
19424
+ }
19425
+ };
19391
19426
  }, [containAtLeastOneMermaid, theme, isHydrated]);
19392
19427
  function handleClick({ target }, index) {
19393
19428
  const $wrap = target.parentElement.parentElement;
@@ -19403,15 +19438,13 @@ const CodeTabs = (props) => {
19403
19438
  const codeComponent = getCodeComponent(childrenArray[0]);
19404
19439
  if (codeComponent?.props?.lang === 'mermaid') {
19405
19440
  const value = codeComponent?.props?.value;
19406
- return external_react_default().createElement("pre", { className: "mermaid-render mermaid_single" }, value);
19441
+ return (external_react_default().createElement("pre", { ref: mermaidRef, className: "mermaid-render mermaid_single" }, value));
19407
19442
  }
19408
19443
  }
19409
19444
  return (external_react_default().createElement("div", { className: `CodeTabs CodeTabs_initial theme-${theme}` },
19410
19445
  external_react_default().createElement("div", { className: "CodeTabs-toolbar" }, childrenArray.map((pre, i) => {
19411
19446
  // the first or only child should be our Code component
19412
- const tabCodeComponent = Array.isArray(pre.props?.children)
19413
- ? pre.props.children[0]
19414
- : pre.props?.children;
19447
+ const tabCodeComponent = Array.isArray(pre.props?.children) ? pre.props.children[0] : pre.props?.children;
19415
19448
  const lang = tabCodeComponent?.props?.lang;
19416
19449
  const meta = tabCodeComponent?.props?.meta;
19417
19450
  /* istanbul ignore next */
@@ -24438,6 +24471,8 @@ const GlossaryContext = (0,external_react_.createContext)([]);
24438
24471
 
24439
24472
  const Glossary = ({ children, term: termProp, terms }) => {
24440
24473
  const term = (Array.isArray(children) ? children[0] : children) || termProp;
24474
+ if (!term)
24475
+ return null;
24441
24476
  const foundTerm = terms.find(i => term.toLowerCase() === i?.term?.toLowerCase());
24442
24477
  if (!foundTerm)
24443
24478
  return external_react_default().createElement("span", null, term);
@@ -24905,19 +24940,10 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
24905
24940
  records.forEach(record => {
24906
24941
  if (record.type === 'childList') {
24907
24942
  record.addedNodes.forEach(node => {
24908
- if (!(node instanceof HTMLElement))
24943
+ if (!(node instanceof HTMLElement) || !node.classList.contains(tailwindPrefix))
24909
24944
  return;
24910
- // Check the added node itself
24911
- if (node.classList.contains(tailwindPrefix)) {
24912
- traverse(node, addClasses);
24913
- shouldUpdate = true;
24914
- }
24915
- // Also check descendants — React may insert a parent node
24916
- // whose children contain TailwindRoot elements
24917
- node.querySelectorAll(`.${tailwindPrefix}`).forEach(child => {
24918
- traverse(child, addClasses);
24919
- shouldUpdate = true;
24920
- });
24945
+ traverse(node, addClasses);
24946
+ shouldUpdate = true;
24921
24947
  });
24922
24948
  }
24923
24949
  else if (record.type === 'attributes') {
@@ -24926,10 +24952,10 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
24926
24952
  addClasses(record.target);
24927
24953
  shouldUpdate = true;
24928
24954
  }
24929
- if (shouldUpdate) {
24930
- setClasses(Array.from(classesSet.current));
24931
- }
24932
24955
  });
24956
+ if (shouldUpdate) {
24957
+ setClasses(Array.from(classesSet.current));
24958
+ }
24933
24959
  });
24934
24960
  observer.observe(ref.current.parentElement, {
24935
24961
  subtree: true,
@@ -73150,6 +73176,10 @@ const plain = (node, opts = {}) => {
73150
73176
  };
73151
73177
  /* harmony default export */ const lib_plain = (plain);
73152
73178
 
73179
+ ;// ./processor/compile/gemoji.ts
73180
+ const gemoji = (node) => `:${node.name}:`;
73181
+ /* harmony default export */ const compile_gemoji = (gemoji);
73182
+
73153
73183
  ;// ./processor/compile/variable.ts
73154
73184
  const variable = (node) => `{user.${node.data?.hProperties?.name || ''}}`;
73155
73185
  /* harmony default export */ const compile_variable = (variable);
@@ -73200,6 +73230,7 @@ const extractText = (node) => {
73200
73230
 
73201
73231
 
73202
73232
 
73233
+
73203
73234
  const titleParser = unified().use(remarkParse).use(remarkGfm);
73204
73235
  // The title paragraph may contain custom AST nodes that `toMarkdown` doesn't
73205
73236
  // natively understand
@@ -73207,8 +73238,8 @@ const toMarkdownExtensions = [
73207
73238
  gfmToMarkdown(),
73208
73239
  // For mdx variable syntaxes (e.g., {user.name})
73209
73240
  mdxExpressionToMarkdown(),
73210
- // Important: This is required and would crash the parser if there's no variable node handler
73211
- { handlers: { [NodeTypes.variable]: compile_variable } },
73241
+ // Important: This is required and would crash the parser if there's no variable and gemoji node handler
73242
+ { handlers: { [NodeTypes.variable]: compile_variable, [NodeTypes.emoji]: compile_gemoji } },
73212
73243
  ];
73213
73244
  const callouts_regex = `^(${emoji_regex().source}|⚠)(\\s+|$)`;
73214
73245
  const findFirst = (node) => {
@@ -73603,7 +73634,7 @@ const embedTransformer = () => {
73603
73634
  *
73604
73635
  * @type {Array<Gemoji>}
73605
73636
  */
73606
- const gemoji = [
73637
+ const gemoji_gemoji = [
73607
73638
  {
73608
73639
  emoji: '😀',
73609
73640
  names: ['grinning'],
@@ -90546,7 +90577,7 @@ class Owlmoji {
90546
90577
  return null;
90547
90578
  };
90548
90579
  static nameToEmoji = nameToEmoji;
90549
- static owlmoji = gemoji.concat(owlmoji);
90580
+ static owlmoji = gemoji_gemoji.concat(owlmoji);
90550
90581
  }
90551
90582
 
90552
90583
  ;// ./processor/transform/gemoji+.ts
@@ -91456,6 +91487,316 @@ const mdxToHast = () => tree => {
91456
91487
  };
91457
91488
  /* harmony default export */ const mdx_to_hast = (mdxToHast);
91458
91489
 
91490
+ ;// ./lib/mdast-util/gemoji/index.ts
91491
+
91492
+
91493
+ function exitGemoji(token) {
91494
+ const name = this.sliceSerialize(token).slice(1, -1); // strip surrounding colons
91495
+ switch (Owlmoji.kind(name)) {
91496
+ case 'gemoji': {
91497
+ this.enter({ type: NodeTypes.emoji, value: Owlmoji.nameToEmoji[name], name }, token);
91498
+ this.exit(token);
91499
+ break;
91500
+ }
91501
+ case 'fontawesome': {
91502
+ this.enter({
91503
+ type: NodeTypes.i,
91504
+ value: name,
91505
+ data: { hName: 'i', hProperties: { className: ['fa-regular', name] } },
91506
+ }, token);
91507
+ this.exit(token);
91508
+ break;
91509
+ }
91510
+ case 'owlmoji': {
91511
+ this.enter({
91512
+ type: NodeTypes.emoji,
91513
+ value: `:${name}:`,
91514
+ name,
91515
+ data: {
91516
+ hName: 'img',
91517
+ hProperties: {
91518
+ src: `/public/img/emojis/${name}.png`,
91519
+ alt: `:${name}:`,
91520
+ title: `:${name}:`,
91521
+ className: 'emoji',
91522
+ align: 'absmiddle',
91523
+ height: '20',
91524
+ width: '20',
91525
+ },
91526
+ },
91527
+ }, token);
91528
+ this.exit(token);
91529
+ break;
91530
+ }
91531
+ default: {
91532
+ // Unknown shortcode, emit as literal text `:name:`
91533
+ this.enter({ type: 'text', value: `:${name}:` }, token);
91534
+ this.exit(token);
91535
+ break;
91536
+ }
91537
+ }
91538
+ }
91539
+ function gemojiFromMarkdown() {
91540
+ return {
91541
+ exit: {
91542
+ gemoji: exitGemoji,
91543
+ },
91544
+ };
91545
+ }
91546
+
91547
+ ;// ./node_modules/micromark-util-symbol/lib/codes.js
91548
+ /**
91549
+ * Character codes.
91550
+ *
91551
+ * This module is compiled away!
91552
+ *
91553
+ * micromark works based on character codes.
91554
+ * This module contains constants for the ASCII block and the replacement
91555
+ * character.
91556
+ * A couple of them are handled in a special way, such as the line endings
91557
+ * (CR, LF, and CR+LF, commonly known as end-of-line: EOLs), the tab (horizontal
91558
+ * tab) and its expansion based on what column it’s at (virtual space),
91559
+ * and the end-of-file (eof) character.
91560
+ * As values are preprocessed before handling them, the actual characters LF,
91561
+ * CR, HT, and NUL (which is present as the replacement character), are
91562
+ * guaranteed to not exist.
91563
+ *
91564
+ * Unicode basic latin block.
91565
+ */
91566
+ const codes = /** @type {const} */ ({
91567
+ carriageReturn: -5,
91568
+ lineFeed: -4,
91569
+ carriageReturnLineFeed: -3,
91570
+ horizontalTab: -2,
91571
+ virtualSpace: -1,
91572
+ eof: null,
91573
+ nul: 0,
91574
+ soh: 1,
91575
+ stx: 2,
91576
+ etx: 3,
91577
+ eot: 4,
91578
+ enq: 5,
91579
+ ack: 6,
91580
+ bel: 7,
91581
+ bs: 8,
91582
+ ht: 9, // `\t`
91583
+ lf: 10, // `\n`
91584
+ vt: 11, // `\v`
91585
+ ff: 12, // `\f`
91586
+ cr: 13, // `\r`
91587
+ so: 14,
91588
+ si: 15,
91589
+ dle: 16,
91590
+ dc1: 17,
91591
+ dc2: 18,
91592
+ dc3: 19,
91593
+ dc4: 20,
91594
+ nak: 21,
91595
+ syn: 22,
91596
+ etb: 23,
91597
+ can: 24,
91598
+ em: 25,
91599
+ sub: 26,
91600
+ esc: 27,
91601
+ fs: 28,
91602
+ gs: 29,
91603
+ rs: 30,
91604
+ us: 31,
91605
+ space: 32,
91606
+ exclamationMark: 33, // `!`
91607
+ quotationMark: 34, // `"`
91608
+ numberSign: 35, // `#`
91609
+ dollarSign: 36, // `$`
91610
+ percentSign: 37, // `%`
91611
+ ampersand: 38, // `&`
91612
+ apostrophe: 39, // `'`
91613
+ leftParenthesis: 40, // `(`
91614
+ rightParenthesis: 41, // `)`
91615
+ asterisk: 42, // `*`
91616
+ plusSign: 43, // `+`
91617
+ comma: 44, // `,`
91618
+ dash: 45, // `-`
91619
+ dot: 46, // `.`
91620
+ slash: 47, // `/`
91621
+ digit0: 48, // `0`
91622
+ digit1: 49, // `1`
91623
+ digit2: 50, // `2`
91624
+ digit3: 51, // `3`
91625
+ digit4: 52, // `4`
91626
+ digit5: 53, // `5`
91627
+ digit6: 54, // `6`
91628
+ digit7: 55, // `7`
91629
+ digit8: 56, // `8`
91630
+ digit9: 57, // `9`
91631
+ colon: 58, // `:`
91632
+ semicolon: 59, // `;`
91633
+ lessThan: 60, // `<`
91634
+ equalsTo: 61, // `=`
91635
+ greaterThan: 62, // `>`
91636
+ questionMark: 63, // `?`
91637
+ atSign: 64, // `@`
91638
+ uppercaseA: 65, // `A`
91639
+ uppercaseB: 66, // `B`
91640
+ uppercaseC: 67, // `C`
91641
+ uppercaseD: 68, // `D`
91642
+ uppercaseE: 69, // `E`
91643
+ uppercaseF: 70, // `F`
91644
+ uppercaseG: 71, // `G`
91645
+ uppercaseH: 72, // `H`
91646
+ uppercaseI: 73, // `I`
91647
+ uppercaseJ: 74, // `J`
91648
+ uppercaseK: 75, // `K`
91649
+ uppercaseL: 76, // `L`
91650
+ uppercaseM: 77, // `M`
91651
+ uppercaseN: 78, // `N`
91652
+ uppercaseO: 79, // `O`
91653
+ uppercaseP: 80, // `P`
91654
+ uppercaseQ: 81, // `Q`
91655
+ uppercaseR: 82, // `R`
91656
+ uppercaseS: 83, // `S`
91657
+ uppercaseT: 84, // `T`
91658
+ uppercaseU: 85, // `U`
91659
+ uppercaseV: 86, // `V`
91660
+ uppercaseW: 87, // `W`
91661
+ uppercaseX: 88, // `X`
91662
+ uppercaseY: 89, // `Y`
91663
+ uppercaseZ: 90, // `Z`
91664
+ leftSquareBracket: 91, // `[`
91665
+ backslash: 92, // `\`
91666
+ rightSquareBracket: 93, // `]`
91667
+ caret: 94, // `^`
91668
+ underscore: 95, // `_`
91669
+ graveAccent: 96, // `` ` ``
91670
+ lowercaseA: 97, // `a`
91671
+ lowercaseB: 98, // `b`
91672
+ lowercaseC: 99, // `c`
91673
+ lowercaseD: 100, // `d`
91674
+ lowercaseE: 101, // `e`
91675
+ lowercaseF: 102, // `f`
91676
+ lowercaseG: 103, // `g`
91677
+ lowercaseH: 104, // `h`
91678
+ lowercaseI: 105, // `i`
91679
+ lowercaseJ: 106, // `j`
91680
+ lowercaseK: 107, // `k`
91681
+ lowercaseL: 108, // `l`
91682
+ lowercaseM: 109, // `m`
91683
+ lowercaseN: 110, // `n`
91684
+ lowercaseO: 111, // `o`
91685
+ lowercaseP: 112, // `p`
91686
+ lowercaseQ: 113, // `q`
91687
+ lowercaseR: 114, // `r`
91688
+ lowercaseS: 115, // `s`
91689
+ lowercaseT: 116, // `t`
91690
+ lowercaseU: 117, // `u`
91691
+ lowercaseV: 118, // `v`
91692
+ lowercaseW: 119, // `w`
91693
+ lowercaseX: 120, // `x`
91694
+ lowercaseY: 121, // `y`
91695
+ lowercaseZ: 122, // `z`
91696
+ leftCurlyBrace: 123, // `{`
91697
+ verticalBar: 124, // `|`
91698
+ rightCurlyBrace: 125, // `}`
91699
+ tilde: 126, // `~`
91700
+ del: 127,
91701
+ // Unicode Specials block.
91702
+ byteOrderMarker: 65_279,
91703
+ // Unicode Specials block.
91704
+ replacementCharacter: 65_533 // `�`
91705
+ })
91706
+
91707
+ ;// ./lib/micromark/gemoji/syntax.ts
91708
+
91709
+ // Matches the name pattern from the original regex: \+1 or [-\w]+
91710
+ // see https://github.com/readmeio/markdown/blob/489a71e19b34f640595ce81e988dad631045186f/processor/transform/gemoji%2B.ts#L9
91711
+ function isNameChar(code) {
91712
+ if (code === null)
91713
+ return false;
91714
+ return ((code >= codes.lowercaseA && code <= codes.lowercaseZ) ||
91715
+ (code >= codes.uppercaseA && code <= codes.uppercaseZ) ||
91716
+ (code >= codes.digit0 && code <= codes.digit9) ||
91717
+ code === codes.dash ||
91718
+ code === codes.underscore);
91719
+ }
91720
+ const gemojiConstruct = {
91721
+ name: 'gemoji',
91722
+ tokenize,
91723
+ };
91724
+ function tokenize(effects, ok, nok) {
91725
+ let hasName = false;
91726
+ // Entry point — expect opening `:`
91727
+ const start = (code) => {
91728
+ if (code !== codes.colon)
91729
+ return nok(code);
91730
+ effects.enter('gemoji');
91731
+ effects.enter('gemojiMarker');
91732
+ effects.consume(code); // :
91733
+ effects.exit('gemojiMarker');
91734
+ effects.enter('gemojiName');
91735
+ return nameStart;
91736
+ };
91737
+ // First char after `:`, branch on `+` for :+1:, otherwise start normal name
91738
+ const nameStart = (code) => {
91739
+ if (code === codes.plusSign) {
91740
+ effects.consume(code); // +
91741
+ return plusOne;
91742
+ }
91743
+ if (isNameChar(code)) {
91744
+ hasName = true;
91745
+ effects.consume(code);
91746
+ return name;
91747
+ }
91748
+ // Not a valid shortcode start (e.g. `::`, `: `, `:<`)
91749
+ return nok(code);
91750
+ };
91751
+ // After `+`, only `1` is valid (for :+1:), anything else rejects
91752
+ // this is a special case for :+1: 👍 since + isnt a normal name character
91753
+ const plusOne = (code) => {
91754
+ if (code === codes.digit1) {
91755
+ hasName = true;
91756
+ effects.consume(code); // 1
91757
+ return nameEnd;
91758
+ }
91759
+ return nok(code);
91760
+ };
91761
+ // Consume name characters until we hit closing `:` or an invalid char
91762
+ const name = (code) => {
91763
+ if (code === codes.colon) {
91764
+ if (!hasName)
91765
+ return nok(code);
91766
+ return nameEnd(code);
91767
+ }
91768
+ if (isNameChar(code)) {
91769
+ effects.consume(code);
91770
+ return name;
91771
+ }
91772
+ // Invalid character in name (space, newline, special char) — reject
91773
+ return nok(code);
91774
+ };
91775
+ // Expect closing `:`
91776
+ const nameEnd = (code) => {
91777
+ if (code !== codes.colon)
91778
+ return nok(code);
91779
+ effects.exit('gemojiName');
91780
+ effects.enter('gemojiMarker');
91781
+ effects.consume(code); // :
91782
+ effects.exit('gemojiMarker');
91783
+ effects.exit('gemoji');
91784
+ return ok;
91785
+ };
91786
+ return start;
91787
+ }
91788
+ function syntax_gemoji() {
91789
+ return {
91790
+ text: { [codes.colon]: gemojiConstruct },
91791
+ };
91792
+ }
91793
+
91794
+ ;// ./lib/micromark/gemoji/index.ts
91795
+ /**
91796
+ * Micromark extension for :emoji: inline syntax.
91797
+ */
91798
+
91799
+
91459
91800
  ;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
91460
91801
 
91461
91802
  // Marker patterns for multi-node emphasis detection
@@ -91812,6 +92153,7 @@ const normalizeEmphasisAST = () => (tree) => {
91812
92153
 
91813
92154
 
91814
92155
 
92156
+
91815
92157
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
91816
92158
  const tableTypes = {
91817
92159
  tr: 'tableRow',
@@ -91819,10 +92161,12 @@ const tableTypes = {
91819
92161
  td: 'tableCell',
91820
92162
  };
91821
92163
  const tableNodeProcessor = unified()
92164
+ .data('micromarkExtensions', [syntax_gemoji()])
92165
+ .data('fromMarkdownExtensions', [gemojiFromMarkdown()])
91822
92166
  .use(remarkParse)
91823
92167
  .use(remarkMdx)
91824
92168
  .use(normalize_malformed_md_syntax)
91825
- .use([[callouts, { isMdxish: true }], gemoji_, code_tabs])
92169
+ .use([[callouts, { isMdxish: true }], code_tabs])
91826
92170
  .use(remarkGfm);
91827
92171
  /**
91828
92172
  * Check if children are only text nodes that might contain markdown
@@ -92695,7 +93039,6 @@ const mdxishTransformers = [
92695
93039
  [callouts, { isMdxish: true }],
92696
93040
  code_tabs,
92697
93041
  transform_images,
92698
- gemoji_,
92699
93042
  ];
92700
93043
  /* harmony default export */ const transform = (Object.values(defaultTransforms));
92701
93044
 
@@ -112477,10 +112820,6 @@ const embed_embed = (node) => {
112477
112820
  };
112478
112821
  /* harmony default export */ const compile_embed = (embed_embed);
112479
112822
 
112480
- ;// ./processor/compile/gemoji.ts
112481
- const gemoji_gemoji = (node) => `:${node.name}:`;
112482
- /* harmony default export */ const compile_gemoji = (gemoji_gemoji);
112483
-
112484
112823
  ;// ./processor/compile/html-block.ts
112485
112824
 
112486
112825
  const htmlBlock = (node) => {
@@ -114463,6 +114802,86 @@ const mdxComponentHandlers = {
114463
114802
  [NodeTypes.htmlBlock]: htmlBlockHandler,
114464
114803
  };
114465
114804
 
114805
+ ;// ./processor/transform/mdxish/close-self-closing-html-tags.ts
114806
+
114807
+ /**
114808
+ * HTML void elements that are legitimately self-closing per the HTML spec.
114809
+ * These should NOT be transformed.
114810
+ *
114811
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#void-elements
114812
+ */
114813
+ const HTML_VOID_ELEMENTS = new Set([
114814
+ 'area',
114815
+ 'base',
114816
+ 'br',
114817
+ 'col',
114818
+ 'embed',
114819
+ 'hr',
114820
+ 'img',
114821
+ 'input',
114822
+ 'link',
114823
+ 'meta',
114824
+ 'param',
114825
+ 'source',
114826
+ 'track',
114827
+ 'wbr',
114828
+ ]);
114829
+ /**
114830
+ * Matches self-closing HTML tags: `<tagname/>` or `<tagname attr="val" />`
114831
+ *
114832
+ * Captures:
114833
+ * 1 - tag name (lowercase letters, digits, hyphens)
114834
+ * 2 - attributes (everything between tag name and `/>`)
114835
+ *
114836
+ * The attribute portion skips over quoted strings (`"..."` and `'...'`) so that
114837
+ * a `/>` inside an attribute value (e.g. `title="use /> here"`) does not cause
114838
+ * a premature match.
114839
+ *
114840
+ * Only matches lowercase tag names to avoid interfering with PascalCase
114841
+ * JSX custom components (e.g. `<MyComponent />`), which are handled
114842
+ * separately by mdxish-component-blocks.
114843
+ */
114844
+ const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)?)?\s*\/>/g;
114845
+ /**
114846
+ * String-level preprocessor that converts self-closing non-void HTML tags
114847
+ * into explicitly closed tags.
114848
+ *
114849
+ * Problem: `rehype-raw` (via parse5) follows the HTML spec where the `/` in
114850
+ * `<i />` is ignored for non-void elements. This causes `<i />` to be parsed
114851
+ * as an opening `<i>` tag, which then wraps all subsequent content.
114852
+ *
114853
+ * Solution: Transform `<i />` → `<i></i>` before parsing, so rehype-raw
114854
+ * sees a properly closed element.
114855
+ *
114856
+ * This preprocessor:
114857
+ * - Skips void elements (`<br />`, `<hr />`, `<img />`, etc.)
114858
+ * - Skips PascalCase tags (custom components handled elsewhere)
114859
+ * - Protects code blocks from transformation
114860
+ *
114861
+ * @example
114862
+ * closeSelfClosingHtmlTags('<i/> text') // '<i></i> text'
114863
+ * closeSelfClosingHtmlTags('<br />') // '<br />' (void, untouched)
114864
+ * closeSelfClosingHtmlTags('<MyComp />') // '<MyComp />' (PascalCase, untouched)
114865
+ * closeSelfClosingHtmlTags('<i class="icon" />') // '<i class="icon"></i>'
114866
+ */
114867
+ function closeSelfClosingHtmlTags(content) {
114868
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
114869
+ const result = protectedContent.replace(SELF_CLOSING_TAG_RE, (match, tagName, attrs) => {
114870
+ // Skip void elements (legitimately self-closing)
114871
+ if (HTML_VOID_ELEMENTS.has(tagName)) {
114872
+ return match;
114873
+ }
114874
+ // Skip tags with hyphens — these are custom elements (web components)
114875
+ // and should not be rewritten (e.g. <my-component />)
114876
+ if (tagName.includes('-')) {
114877
+ return match;
114878
+ }
114879
+ const attributes = attrs?.trim() ? ` ${attrs.trim()}` : '';
114880
+ return `<${tagName}${attributes}></${tagName}>`;
114881
+ });
114882
+ return restoreCodeBlocks(result, protectedCode);
114883
+ }
114884
+
114466
114885
  ;// ./processor/transform/mdxish/evaluate-expressions.ts
114467
114886
 
114468
114887
 
@@ -114752,166 +115171,6 @@ function legacyVariableFromMarkdown() {
114752
115171
  };
114753
115172
  }
114754
115173
 
114755
- ;// ./node_modules/micromark-util-symbol/lib/codes.js
114756
- /**
114757
- * Character codes.
114758
- *
114759
- * This module is compiled away!
114760
- *
114761
- * micromark works based on character codes.
114762
- * This module contains constants for the ASCII block and the replacement
114763
- * character.
114764
- * A couple of them are handled in a special way, such as the line endings
114765
- * (CR, LF, and CR+LF, commonly known as end-of-line: EOLs), the tab (horizontal
114766
- * tab) and its expansion based on what column it’s at (virtual space),
114767
- * and the end-of-file (eof) character.
114768
- * As values are preprocessed before handling them, the actual characters LF,
114769
- * CR, HT, and NUL (which is present as the replacement character), are
114770
- * guaranteed to not exist.
114771
- *
114772
- * Unicode basic latin block.
114773
- */
114774
- const codes = /** @type {const} */ ({
114775
- carriageReturn: -5,
114776
- lineFeed: -4,
114777
- carriageReturnLineFeed: -3,
114778
- horizontalTab: -2,
114779
- virtualSpace: -1,
114780
- eof: null,
114781
- nul: 0,
114782
- soh: 1,
114783
- stx: 2,
114784
- etx: 3,
114785
- eot: 4,
114786
- enq: 5,
114787
- ack: 6,
114788
- bel: 7,
114789
- bs: 8,
114790
- ht: 9, // `\t`
114791
- lf: 10, // `\n`
114792
- vt: 11, // `\v`
114793
- ff: 12, // `\f`
114794
- cr: 13, // `\r`
114795
- so: 14,
114796
- si: 15,
114797
- dle: 16,
114798
- dc1: 17,
114799
- dc2: 18,
114800
- dc3: 19,
114801
- dc4: 20,
114802
- nak: 21,
114803
- syn: 22,
114804
- etb: 23,
114805
- can: 24,
114806
- em: 25,
114807
- sub: 26,
114808
- esc: 27,
114809
- fs: 28,
114810
- gs: 29,
114811
- rs: 30,
114812
- us: 31,
114813
- space: 32,
114814
- exclamationMark: 33, // `!`
114815
- quotationMark: 34, // `"`
114816
- numberSign: 35, // `#`
114817
- dollarSign: 36, // `$`
114818
- percentSign: 37, // `%`
114819
- ampersand: 38, // `&`
114820
- apostrophe: 39, // `'`
114821
- leftParenthesis: 40, // `(`
114822
- rightParenthesis: 41, // `)`
114823
- asterisk: 42, // `*`
114824
- plusSign: 43, // `+`
114825
- comma: 44, // `,`
114826
- dash: 45, // `-`
114827
- dot: 46, // `.`
114828
- slash: 47, // `/`
114829
- digit0: 48, // `0`
114830
- digit1: 49, // `1`
114831
- digit2: 50, // `2`
114832
- digit3: 51, // `3`
114833
- digit4: 52, // `4`
114834
- digit5: 53, // `5`
114835
- digit6: 54, // `6`
114836
- digit7: 55, // `7`
114837
- digit8: 56, // `8`
114838
- digit9: 57, // `9`
114839
- colon: 58, // `:`
114840
- semicolon: 59, // `;`
114841
- lessThan: 60, // `<`
114842
- equalsTo: 61, // `=`
114843
- greaterThan: 62, // `>`
114844
- questionMark: 63, // `?`
114845
- atSign: 64, // `@`
114846
- uppercaseA: 65, // `A`
114847
- uppercaseB: 66, // `B`
114848
- uppercaseC: 67, // `C`
114849
- uppercaseD: 68, // `D`
114850
- uppercaseE: 69, // `E`
114851
- uppercaseF: 70, // `F`
114852
- uppercaseG: 71, // `G`
114853
- uppercaseH: 72, // `H`
114854
- uppercaseI: 73, // `I`
114855
- uppercaseJ: 74, // `J`
114856
- uppercaseK: 75, // `K`
114857
- uppercaseL: 76, // `L`
114858
- uppercaseM: 77, // `M`
114859
- uppercaseN: 78, // `N`
114860
- uppercaseO: 79, // `O`
114861
- uppercaseP: 80, // `P`
114862
- uppercaseQ: 81, // `Q`
114863
- uppercaseR: 82, // `R`
114864
- uppercaseS: 83, // `S`
114865
- uppercaseT: 84, // `T`
114866
- uppercaseU: 85, // `U`
114867
- uppercaseV: 86, // `V`
114868
- uppercaseW: 87, // `W`
114869
- uppercaseX: 88, // `X`
114870
- uppercaseY: 89, // `Y`
114871
- uppercaseZ: 90, // `Z`
114872
- leftSquareBracket: 91, // `[`
114873
- backslash: 92, // `\`
114874
- rightSquareBracket: 93, // `]`
114875
- caret: 94, // `^`
114876
- underscore: 95, // `_`
114877
- graveAccent: 96, // `` ` ``
114878
- lowercaseA: 97, // `a`
114879
- lowercaseB: 98, // `b`
114880
- lowercaseC: 99, // `c`
114881
- lowercaseD: 100, // `d`
114882
- lowercaseE: 101, // `e`
114883
- lowercaseF: 102, // `f`
114884
- lowercaseG: 103, // `g`
114885
- lowercaseH: 104, // `h`
114886
- lowercaseI: 105, // `i`
114887
- lowercaseJ: 106, // `j`
114888
- lowercaseK: 107, // `k`
114889
- lowercaseL: 108, // `l`
114890
- lowercaseM: 109, // `m`
114891
- lowercaseN: 110, // `n`
114892
- lowercaseO: 111, // `o`
114893
- lowercaseP: 112, // `p`
114894
- lowercaseQ: 113, // `q`
114895
- lowercaseR: 114, // `r`
114896
- lowercaseS: 115, // `s`
114897
- lowercaseT: 116, // `t`
114898
- lowercaseU: 117, // `u`
114899
- lowercaseV: 118, // `v`
114900
- lowercaseW: 119, // `w`
114901
- lowercaseX: 120, // `x`
114902
- lowercaseY: 121, // `y`
114903
- lowercaseZ: 122, // `z`
114904
- leftCurlyBrace: 123, // `{`
114905
- verticalBar: 124, // `|`
114906
- rightCurlyBrace: 125, // `}`
114907
- tilde: 126, // `~`
114908
- del: 127,
114909
- // Unicode Specials block.
114910
- byteOrderMarker: 65_279,
114911
- // Unicode Specials block.
114912
- replacementCharacter: 65_533 // `�`
114913
- })
114914
-
114915
115174
  ;// ./lib/micromark/legacy-variable/syntax.ts
114916
115175
 
114917
115176
 
@@ -114923,9 +115182,9 @@ function isAllowedValueChar(code) {
114923
115182
  }
114924
115183
  const legacyVariableConstruct = {
114925
115184
  name: 'legacyVariable',
114926
- tokenize,
115185
+ tokenize: syntax_tokenize,
114927
115186
  };
114928
- function tokenize(effects, ok, nok) {
115187
+ function syntax_tokenize(effects, ok, nok) {
114929
115188
  let hasValue = false;
114930
115189
  const start = (code) => {
114931
115190
  if (code !== codes.lessThan)
@@ -116064,6 +116323,8 @@ const EMPTY_CODE_PLACEHOLDER = {
116064
116323
 
116065
116324
 
116066
116325
 
116326
+
116327
+
116067
116328
 
116068
116329
 
116069
116330
 
@@ -116106,8 +116367,8 @@ const preprocessBody = (text) => {
116106
116367
  };
116107
116368
  /** Markdown parser */
116108
116369
  const contentParser = unified()
116109
- .data('micromarkExtensions', [legacyVariable(), looseHtmlEntity()])
116110
- .data('fromMarkdownExtensions', [legacyVariableFromMarkdown(), emptyTaskListItemFromMarkdown(), looseHtmlEntityFromMarkdown()])
116370
+ .data('micromarkExtensions', [syntax_gemoji(), legacyVariable(), looseHtmlEntity()])
116371
+ .data('fromMarkdownExtensions', [gemojiFromMarkdown(), legacyVariableFromMarkdown(), emptyTaskListItemFromMarkdown(), looseHtmlEntityFromMarkdown()])
116111
116372
  .use(remarkParse)
116112
116373
  .use(remarkBreaks)
116113
116374
  .use(remarkGfm)
@@ -116120,8 +116381,8 @@ const contentParser = unified()
116120
116381
  * such as `<ul><li>https://a</li>\n</ul>` due to subtokenizing recursion for URLs
116121
116382
  */
116122
116383
  const markdownToHtml = unified()
116123
- .data('micromarkExtensions', [gfmStrikethrough(), legacyVariable(), looseHtmlEntity()])
116124
- .data('fromMarkdownExtensions', [gfmStrikethroughFromMarkdown(), legacyVariableFromMarkdown(), emptyTaskListItemFromMarkdown(), looseHtmlEntityFromMarkdown()])
116384
+ .data('micromarkExtensions', [gfmStrikethrough(), syntax_gemoji(), legacyVariable(), looseHtmlEntity()])
116385
+ .data('fromMarkdownExtensions', [gfmStrikethroughFromMarkdown(), gemojiFromMarkdown(), legacyVariableFromMarkdown(), emptyTaskListItemFromMarkdown(), looseHtmlEntityFromMarkdown()])
116125
116386
  .use(remarkParse)
116126
116387
  .use(normalize_malformed_md_syntax)
116127
116388
  .use(remarkRehype)
@@ -116256,11 +116517,34 @@ const parseBlock = (text) => {
116256
116517
  const tree = contentParser.runSync(contentParser.parse(text));
116257
116518
  return tree.children;
116258
116519
  };
116259
- const parseInline = (text) => {
116520
+ /**
116521
+ * Minimal parser for api-header titles.
116522
+ * Disables markdown constructs that are not parsed in legacy (headings, lists)
116523
+ */
116524
+ const apiHeaderTitleParser = unified()
116525
+ .data('micromarkExtensions', [
116526
+ syntax_gemoji(),
116527
+ legacyVariable(),
116528
+ looseHtmlEntity(),
116529
+ {
116530
+ disable: {
116531
+ null: [
116532
+ 'blockQuote',
116533
+ 'headingAtx',
116534
+ 'list',
116535
+ 'thematicBreak',
116536
+ ],
116537
+ },
116538
+ },
116539
+ ])
116540
+ .data('fromMarkdownExtensions', [gemojiFromMarkdown(), legacyVariableFromMarkdown(), looseHtmlEntityFromMarkdown()])
116541
+ .use(remarkParse)
116542
+ .use(remarkGfm);
116543
+ const parseApiHeaderTitle = (text) => {
116260
116544
  if (!text.trim())
116261
116545
  return textToInline(text);
116262
- const tree = contentParser.runSync(contentParser.parse(text));
116263
- return tree.children;
116546
+ const tree = apiHeaderTitleParser.runSync(apiHeaderTitleParser.parse(text));
116547
+ return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
116264
116548
  };
116265
116549
  /**
116266
116550
  * Transform a magicBlock node into final MDAST nodes.
@@ -116319,7 +116603,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
116319
116603
  const depth = headerJson.level || (compatibilityMode ? 1 : 2);
116320
116604
  return [
116321
116605
  wrapPinnedBlocks({
116322
- children: 'title' in headerJson ? parseInline(headerJson.title || '') : [],
116606
+ children: 'title' in headerJson ? parseApiHeaderTitle(headerJson.title || '') : [],
116323
116607
  depth,
116324
116608
  type: 'heading',
116325
116609
  }, data),
@@ -117367,6 +117651,179 @@ function mdxish_jsx_to_mdast_extractText(nodes) {
117367
117651
  })
117368
117652
  .join('');
117369
117653
  }
117654
+ const FIGURE_OPEN_REGEX = /^<figure(\s[^>]*)?>$/;
117655
+ const FIGURE_CLOSE_REGEX = /^\s*<\/figure>\s*$/;
117656
+ const FIGURE_COMPLETE_REGEX = /^<figure(\s[^>]*)?>[\s\S]*<\/figure>\s*$/;
117657
+ const FIGCAPTION_REGEX = /<figcaption>(.*?)<\/figcaption>/s;
117658
+ const FIGCAPTION_OPEN_REGEX = /^<figcaption>$/;
117659
+ const FIGCAPTION_CLOSE_REGEX = /^<\/figcaption>$/;
117660
+ /**
117661
+ * Extracts an image or image-block from a node. If the node itself is an image/image-block,
117662
+ * it is returned directly. If the node is a paragraph, its children are searched for one.
117663
+ * This is needed because standalone markdown images (`![](url)`) may be wrapped in a
117664
+ * paragraph node by the parser, or already transformed to image-block by imageTransformer.
117665
+ */
117666
+ function findImageInNode(node) {
117667
+ if (node.type === 'image' || node.type === NodeTypes.imageBlock)
117668
+ return node;
117669
+ if (node.type === 'paragraph') {
117670
+ return node.children.find(child => child.type === 'image' || child.type === NodeTypes.imageBlock);
117671
+ }
117672
+ return undefined;
117673
+ }
117674
+ /**
117675
+ * Parses a complete `<figure>` HTML string into image URL, alt text, and optional caption.
117676
+ * Returns undefined if the HTML doesn't contain a recognizable image.
117677
+ */
117678
+ function parseCompleteFigure(html) {
117679
+ const imageMatch = /!\[([^\]]*)\]\(([^)]+)\)/.exec(html);
117680
+ if (!imageMatch)
117681
+ return undefined;
117682
+ const captionMatch = FIGCAPTION_REGEX.exec(html);
117683
+ return {
117684
+ alt: imageMatch[1],
117685
+ caption: captionMatch?.[1],
117686
+ url: imageMatch[2],
117687
+ };
117688
+ }
117689
+ /**
117690
+ * Builds a FigureNode containing an image and optional figcaption from parsed figure data.
117691
+ */
117692
+ function buildFigureNode(imageNode, captionText, position) {
117693
+ const figureChildren = [imageNode];
117694
+ if (captionText) {
117695
+ figureChildren.push({
117696
+ children: [{ type: 'text', value: captionText }],
117697
+ data: { hName: 'figcaption' },
117698
+ type: 'figcaption',
117699
+ });
117700
+ }
117701
+ return {
117702
+ children: figureChildren,
117703
+ data: { hName: 'figure' },
117704
+ position,
117705
+ type: 'figure',
117706
+ };
117707
+ }
117708
+ /**
117709
+ * Scans siblings starting at `startIndex` within a parent's children looking for the
117710
+ * figcaption text and </figure> closing tag. Handles three fragmentation patterns:
117711
+ *
117712
+ * 1. Combined: `<figcaption>Hello</figcaption>\n</figure>` in one html node
117713
+ * 2. Separate: `<figcaption>Hello</figcaption>` then `</figure>` as sibling html nodes
117714
+ * 3. Split (table cells): `<figcaption>` + text(Hello) + `</figcaption>` + `</figure>` as siblings
117715
+ */
117716
+ function scanForFigcaptionAndClose(children, startIndex) {
117717
+ let captionText;
117718
+ let endIndex = startIndex - 1;
117719
+ let foundClose = false;
117720
+ for (let j = startIndex; j < children.length; j += 1) {
117721
+ const sibling = children[j];
117722
+ const htmlValue = sibling.type === 'html' ? sibling.value : undefined;
117723
+ if (htmlValue === undefined && sibling.type !== 'text')
117724
+ break;
117725
+ // Standalone </figure>
117726
+ if (htmlValue && FIGURE_CLOSE_REGEX.test(htmlValue)) {
117727
+ endIndex = j;
117728
+ foundClose = true;
117729
+ break;
117730
+ }
117731
+ // Combined <figcaption>...</figcaption> in one node (possibly with </figure>)
117732
+ if (htmlValue) {
117733
+ const figcaptionMatch = FIGCAPTION_REGEX.exec(htmlValue);
117734
+ if (figcaptionMatch) {
117735
+ captionText = figcaptionMatch[1];
117736
+ if (FIGURE_CLOSE_REGEX.test(htmlValue.replace(FIGCAPTION_REGEX, ''))) {
117737
+ endIndex = j;
117738
+ foundClose = true;
117739
+ break;
117740
+ }
117741
+ const nextSibling = children[j + 1];
117742
+ if (nextSibling?.type === 'html' && FIGURE_CLOSE_REGEX.test(nextSibling.value)) {
117743
+ endIndex = j + 1;
117744
+ foundClose = true;
117745
+ break;
117746
+ }
117747
+ }
117748
+ }
117749
+ // Split figcaption: <figcaption> + text + </figcaption> as separate nodes (table cells)
117750
+ if (htmlValue && FIGCAPTION_OPEN_REGEX.test(htmlValue)) {
117751
+ const textNode = children[j + 1];
117752
+ const closeCaption = children[j + 2];
117753
+ if (textNode?.type === 'text' &&
117754
+ closeCaption?.type === 'html' &&
117755
+ FIGCAPTION_CLOSE_REGEX.test(closeCaption.value)) {
117756
+ captionText = textNode.value;
117757
+ const closeFigure = children[j + 3];
117758
+ if (closeFigure?.type === 'html' && FIGURE_CLOSE_REGEX.test(closeFigure.value)) {
117759
+ endIndex = j + 3;
117760
+ foundClose = true;
117761
+ break;
117762
+ }
117763
+ }
117764
+ }
117765
+ }
117766
+ return { captionText, endIndex, foundClose };
117767
+ }
117768
+ /**
117769
+ * Reconstruct fragmented or complete HTML <figure> elements into figure MDAST nodes.
117770
+ *
117771
+ * Handles three html-based cases (the JSX case is handled by transformFigure in COMPONENT_MAP):
117772
+ * 1. Complete: A single html node containing the full `<figure>...<figcaption>...</figure>` block
117773
+ * (e.g. inside callouts where blockquote parsing keeps it together).
117774
+ * 2. Fragmented siblings: `terminateHtmlFlowBlocks` splits `<figure>` into separate sibling
117775
+ * nodes (open tag, image, figcaption, close tag).
117776
+ * 3. Split tags (GFM table cells): Each tag becomes its own html node with text nodes between them.
117777
+ *
117778
+ * Runs on all parent nodes so it works inside callouts, tables, and at root level.
117779
+ */
117780
+ function reassembleHtmlFigures(tree) {
117781
+ // Case 1: Handle complete <figure> blocks in a single html node (e.g. inside callouts)
117782
+ visit(tree, 'html', (node, index, parent) => {
117783
+ if (!parent || index === undefined)
117784
+ return;
117785
+ if (!FIGURE_COMPLETE_REGEX.test(node.value.trim()))
117786
+ return;
117787
+ const parsed = parseCompleteFigure(node.value);
117788
+ if (!parsed)
117789
+ return;
117790
+ const imageNode = {
117791
+ type: 'image',
117792
+ url: parsed.url,
117793
+ alt: parsed.alt,
117794
+ };
117795
+ const figureNode = buildFigureNode(imageNode, parsed.caption, node.position);
117796
+ parent.children.splice(index, 1, figureNode);
117797
+ });
117798
+ // Case 2 & 3: Handle fragmented <figure> blocks split across sibling nodes
117799
+ const processChildren = (parent) => {
117800
+ const children = parent.children;
117801
+ let i = 0;
117802
+ while (i < children.length) {
117803
+ const node = children[i];
117804
+ const nextNode = children[i + 1];
117805
+ const imageNode = nextNode ? findImageInNode(nextNode) : undefined;
117806
+ if (node.type === 'html' &&
117807
+ FIGURE_OPEN_REGEX.test(node.value.trim()) &&
117808
+ imageNode) {
117809
+ const { captionText, endIndex, foundClose } = scanForFigcaptionAndClose(children, i + 2);
117810
+ if (foundClose) {
117811
+ const figureNode = buildFigureNode(imageNode, captionText, node.position);
117812
+ parent.children.splice(i, endIndex - i + 1, figureNode);
117813
+ }
117814
+ }
117815
+ i += 1;
117816
+ }
117817
+ };
117818
+ // Process all parent nodes (root, callouts, blockquotes, table cells, list items)
117819
+ visit(tree, (node) => {
117820
+ if ('children' in node)
117821
+ processChildren(node);
117822
+ });
117823
+ }
117824
+ /**
117825
+ * Transforms an inline `<Anchor>` JSX element into a readme-anchor MDAST node.
117826
+ */
117370
117827
  const transformAnchor = (jsx) => {
117371
117828
  const attrs = getAttrs(jsx);
117372
117829
  const { href = '', label, target, title } = attrs;
@@ -117385,6 +117842,10 @@ const transformAnchor = (jsx) => {
117385
117842
  position: jsx.position,
117386
117843
  };
117387
117844
  };
117845
+ /**
117846
+ * Transforms an `<Image />` JSX element into an image-block MDAST node.
117847
+ * Normalizes attributes (align, border, width→sizing) and parses caption markdown into children.
117848
+ */
117388
117849
  const transformImage = (jsx) => {
117389
117850
  const attrs = getAttrs(jsx);
117390
117851
  const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
@@ -117424,6 +117885,9 @@ const transformImage = (jsx) => {
117424
117885
  position: jsx.position,
117425
117886
  };
117426
117887
  };
117888
+ /**
117889
+ * Transforms a `<Callout>` JSX element into an rdme-callout MDAST node.
117890
+ */
117427
117891
  const transformCallout = (jsx) => {
117428
117892
  const attrs = getAttrs(jsx);
117429
117893
  const { empty = false, icon = '', theme = '' } = attrs;
@@ -117441,6 +117905,9 @@ const transformCallout = (jsx) => {
117441
117905
  position: jsx.position,
117442
117906
  };
117443
117907
  };
117908
+ /**
117909
+ * Transforms an `<Embed />` JSX element into an embed-block MDAST node.
117910
+ */
117444
117911
  const transformEmbed = (jsx) => {
117445
117912
  const attrs = getAttrs(jsx);
117446
117913
  const { favicon, height, href, html, iframe, image, providerName, providerUrl, provider, title = '', typeOfEmbed, url = '', width, } = attrs;
@@ -117469,6 +117936,9 @@ const transformEmbed = (jsx) => {
117469
117936
  position: jsx.position,
117470
117937
  };
117471
117938
  };
117939
+ /**
117940
+ * Transforms a `<Recipe />` JSX element into a recipe MDAST node.
117941
+ */
117472
117942
  const transformRecipe = (jsx) => {
117473
117943
  const attrs = getAttrs(jsx);
117474
117944
  const { backgroundColor = '', emoji = '', id = '', link = '', slug = '', title = '' } = attrs;
@@ -117605,21 +118075,44 @@ const transformTable = (jsx) => {
117605
118075
  children: rows,
117606
118076
  };
117607
118077
  };
118078
+ /**
118079
+ * Transforms a `<figure>` JSX element into a FigureNode.
118080
+ * Inside JSX tables with blank lines, the parser treats `<figure>` as an mdxJsxFlowElement
118081
+ * containing a paragraph with the image and a `<figcaption>` mdxJsxTextElement as children.
118082
+ */
118083
+ const transformFigure = (jsx) => {
118084
+ let imageNode;
118085
+ let captionText;
118086
+ visit(jsx, (child) => {
118087
+ if (!imageNode && (child.type === 'image' || child.type === NodeTypes.imageBlock)) {
118088
+ imageNode = child;
118089
+ }
118090
+ if (!captionText && child.type === 'mdxJsxTextElement' && child.name === 'figcaption') {
118091
+ captionText = mdxish_jsx_to_mdast_extractText(child.children);
118092
+ }
118093
+ });
118094
+ if (!imageNode)
118095
+ return null;
118096
+ return buildFigureNode(imageNode, captionText, jsx.position);
118097
+ };
117608
118098
  const COMPONENT_MAP = {
117609
118099
  Callout: transformCallout,
117610
118100
  Embed: transformEmbed,
117611
118101
  Image: transformImage,
118102
+ figure: transformFigure,
117612
118103
  Recipe: transformRecipe,
117613
118104
  Table: transformTable,
117614
118105
  };
117615
118106
  /**
117616
- * Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
118107
+ * Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
118108
+ * into proper MDAST node types.
117617
118109
  *
117618
118110
  * This transformer runs after mdxishComponentBlocks and converts:
117619
- * - JSX component elements (Image, Callout, Embed, Recipe) into their corresponding MDAST types
118111
+ * - JSX component elements (Image, Callout, Embed, Recipe, figure) into their corresponding MDAST types
117620
118112
  * - Magic block image nodes (type: 'image') into image-block
117621
118113
  * - Magic block embed nodes (type: 'embed') into embed-block
117622
- * - Figure nodes (magic blocks with captions) into flat image-block with caption string
118114
+ * - Fragmented HTML <figure> blocks (from terminateHtmlFlowBlocks) back into figure nodes
118115
+ * - Figure nodes (from magic blocks, HTML, or JSX) into flat image-block with caption string
117623
118116
  * - Normalizes all image-block attrs (border, align, sizing, caption) to a consistent shape
117624
118117
  *
117625
118118
  * This is controlled by the `newEditorTypes` flag to maintain backwards compatibility.
@@ -117666,6 +118159,9 @@ const mdxishJsxToMdast = () => tree => {
117666
118159
  parent.children[index] = newNode;
117667
118160
  return SKIP;
117668
118161
  });
118162
+ // Reassembling fragmented HTML <figure> blocks into proper figure/figcaption nodes
118163
+ // this will then later be transformed into image-block nodes by imageTransformer
118164
+ reassembleHtmlFigures(tree);
117669
118165
  // Flatten figure nodes (magic blocks with captions) into image-block nodes
117670
118166
  const isFigure = (node) => node.type === 'figure';
117671
118167
  visit(tree, isFigure, (node, index, parent) => {
@@ -117918,6 +118414,7 @@ function restoreSnakeCase(placeholderName, mapping) {
117918
118414
  ;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
117919
118415
 
117920
118416
 
118417
+ const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
117921
118418
  const mdxish_tables_to_jsx_alignToStyle = (align) => {
117922
118419
  if (!align || align === 'left')
117923
118420
  return null;
@@ -117944,7 +118441,7 @@ const mdxishTablesToJsx = () => tree => {
117944
118441
  visit(tree, (node) => ['table', 'tableau'].includes(node.type), (table, index, parent) => {
117945
118442
  let hasFlowContent = false;
117946
118443
  visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
117947
- if (cell.children.length === 0)
118444
+ if (hasFlowContent || cell.children.length === 0)
117948
118445
  return;
117949
118446
  const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
117950
118447
  ? cell.children[0].children[0]
@@ -117955,17 +118452,21 @@ const mdxishTablesToJsx = () => tree => {
117955
118452
  breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
117956
118453
  });
117957
118454
  if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
117958
- if (content.type === 'html')
117959
- return;
117960
- hasFlowContent = true;
117961
- return EXIT;
117962
- }
117963
- visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
117964
- if (node.value.match(/\n/)) {
118455
+ // Plain HTML (e.g. <div>Hello</div>) is skipped here — it stays in GFM cells fine.
118456
+ // But self-closing JSX components (e.g. <Image src="..." caption="..." />) serialize
118457
+ // with newlines that break GFM cells, so they must trigger JSX <Table> serialization.
118458
+ const isPlainHtml = content.type === 'html' && !SELF_CLOSING_JSX_REGEX.test(content.value);
118459
+ if (!isPlainHtml) {
117965
118460
  hasFlowContent = true;
117966
- return EXIT;
117967
118461
  }
117968
- });
118462
+ }
118463
+ if (!hasFlowContent) {
118464
+ visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
118465
+ if (node.value.match(/\n/)) {
118466
+ hasFlowContent = true;
118467
+ }
118468
+ });
118469
+ }
117969
118470
  });
117970
118471
  if (!hasFlowContent) {
117971
118472
  table.type = 'table';
@@ -120205,13 +120706,14 @@ function loadComponents() {
120205
120706
 
120206
120707
 
120207
120708
 
120709
+
120710
+
120208
120711
 
120209
120712
 
120210
120713
 
120211
120714
  const defaultTransformers = [
120212
120715
  [callouts, { isMdxish: true }],
120213
120716
  code_tabs,
120214
- gemoji_,
120215
120717
  transform_embeds,
120216
120718
  ];
120217
120719
  /**
@@ -120221,13 +120723,15 @@ const defaultTransformers = [
120221
120723
  * Runs a series of string-level transformations before micromark/remark parsing:
120222
120724
  * 1. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
120223
120725
  * 2. Terminate HTML flow blocks so subsequent content isn't swallowed
120224
- * 3. Evaluate JSX expressions in attributes (unless safeMode)
120225
- * 4. Replace snake_case component names with parser-safe placeholders
120726
+ * 3. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
120727
+ * 4. Evaluate JSX expressions in attributes (unless safeMode)
120728
+ * 5. Replace snake_case component names with parser-safe placeholders
120226
120729
  */
120227
120730
  function preprocessContent(content, opts) {
120228
120731
  const { safeMode, jsxContext, knownComponents } = opts;
120229
120732
  let result = normalizeTableSeparator(content);
120230
120733
  result = terminateHtmlFlowBlocks(result);
120734
+ result = closeSelfClosingHtmlTags(result);
120231
120735
  result = safeMode ? result : preprocessJSXExpressions(result, jsxContext);
120232
120736
  return processSnakeCaseComponent(result, { knownComponents });
120233
120737
  }
@@ -120257,12 +120761,13 @@ function mdxishAstProcessor(mdContent, opts = {}) {
120257
120761
  };
120258
120762
  const processor = unified()
120259
120763
  .data('micromarkExtensions', safeMode
120260
- ? [jsxTable(), magicBlock(), legacyVariable(), looseHtmlEntity()]
120261
- : [jsxTable(), magicBlock(), mdxExprTextOnly, legacyVariable(), looseHtmlEntity()])
120764
+ ? [jsxTable(), magicBlock(), syntax_gemoji(), legacyVariable(), looseHtmlEntity()]
120765
+ : [jsxTable(), magicBlock(), syntax_gemoji(), mdxExprTextOnly, legacyVariable(), looseHtmlEntity()])
120262
120766
  .data('fromMarkdownExtensions', safeMode
120263
120767
  ? [
120264
120768
  jsxTableFromMarkdown(),
120265
120769
  magicBlockFromMarkdown(),
120770
+ gemojiFromMarkdown(),
120266
120771
  legacyVariableFromMarkdown(),
120267
120772
  emptyTaskListItemFromMarkdown(),
120268
120773
  looseHtmlEntityFromMarkdown(),
@@ -120270,6 +120775,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
120270
120775
  : [
120271
120776
  jsxTableFromMarkdown(),
120272
120777
  magicBlockFromMarkdown(),
120778
+ gemojiFromMarkdown(),
120273
120779
  mdxExpressionFromMarkdown(),
120274
120780
  legacyVariableFromMarkdown(),
120275
120781
  emptyTaskListItemFromMarkdown(),