@aiquants/markdown 5.0.0 → 6.1.0

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.
Files changed (179) hide show
  1. package/README.md +176 -14
  2. package/dist/{abnfDiagram-VRR7QNED-DeHJ63Vu.js → abnfDiagram-VRR7QNED-0ow9i3L-.js} +3 -3
  3. package/dist/{abnfDiagram-VRR7QNED-CfVjEUzG.cjs → abnfDiagram-VRR7QNED-DRNsVB-u.cjs} +1 -1
  4. package/dist/{arc-XFFLkHkK.cjs → arc-BWR9_qMf.cjs} +1 -1
  5. package/dist/arc-Bh12k776.js +83 -0
  6. package/dist/{architectureDiagram-ZJ3FMSHR-CQ1m3MUB.js → architectureDiagram-ZJ3FMSHR-CsFXQcxx.js} +123 -123
  7. package/dist/{architectureDiagram-ZJ3FMSHR-7xJ6YYGu.cjs → architectureDiagram-ZJ3FMSHR-DtrqDlxV.cjs} +1 -1
  8. package/dist/{blockDiagram-677ZJIJ3-CJOFqwiW.cjs → blockDiagram-677ZJIJ3-CoZEeE0C.cjs} +1 -1
  9. package/dist/{blockDiagram-677ZJIJ3-BPC3se0e.js → blockDiagram-677ZJIJ3-CqQ43viF.js} +166 -166
  10. package/dist/{c4Diagram-LMCZKHZV-D8ytYGhR.cjs → c4Diagram-LMCZKHZV-DGA2gxp-.cjs} +1 -1
  11. package/dist/{c4Diagram-LMCZKHZV-BEIrp4t6.js → c4Diagram-LMCZKHZV-QPgNg4El.js} +2 -2
  12. package/dist/{channel-CJQcZr8o.cjs → channel-Jkgg7NV4.cjs} +1 -1
  13. package/dist/channel-PnA15jK3.js +5 -0
  14. package/dist/{chunk-2Q5K7J3B-BbMQR3Sm.cjs → chunk-2Q5K7J3B-1iQ5h7FK.cjs} +1 -1
  15. package/dist/{chunk-2Q5K7J3B-l8ly7Yjr.js → chunk-2Q5K7J3B-DOWfHP5s.js} +1 -1
  16. package/dist/{chunk-32BRIVSS-BhQJBlVw.js → chunk-32BRIVSS-94iTIA-u.js} +1 -1
  17. package/dist/{chunk-32BRIVSS-BwDfmprN.cjs → chunk-32BRIVSS-XUAL4SUU.cjs} +1 -1
  18. package/dist/{chunk-5VM5RSS4-CqDQlS7v.cjs → chunk-5VM5RSS4-CKgpuXOS.cjs} +1 -1
  19. package/dist/{chunk-5VM5RSS4-Bfis-x7E.js → chunk-5VM5RSS4-OANQKmQE.js} +1 -1
  20. package/dist/{chunk-EX3LRPZG-DUTbzwDM.js → chunk-EX3LRPZG-BQDQ1MdS.js} +50 -50
  21. package/dist/{chunk-EX3LRPZG-9aDULZwJ.cjs → chunk-EX3LRPZG-BrRRbskW.cjs} +1 -1
  22. package/dist/{chunk-JWPE2WC7-5cM5nFf6.cjs → chunk-JWPE2WC7-Bz9dXsLZ.cjs} +1 -1
  23. package/dist/{chunk-JWPE2WC7-DpcSSOP5.js → chunk-JWPE2WC7-Djg6Qi23.js} +1 -1
  24. package/dist/{chunk-MOJQB5TN-D_h6bolU.js → chunk-MOJQB5TN-1pZNp2Wl.js} +9 -9
  25. package/dist/{chunk-MOJQB5TN-Cw864mUL.cjs → chunk-MOJQB5TN-DUk2Jy4_.cjs} +1 -1
  26. package/dist/{chunk-PUDLZKDR-yLvT3c9x.cjs → chunk-PUDLZKDR-CVedetzB.cjs} +1 -1
  27. package/dist/{chunk-PUDLZKDR-B0wZjmzv.js → chunk-PUDLZKDR-G5JYyEiR.js} +22 -22
  28. package/dist/{chunk-RYQCIY6F-DTKYsCgk.js → chunk-RYQCIY6F-Crmt36sO.js} +6 -6
  29. package/dist/{chunk-RYQCIY6F-hj9dRWMT.cjs → chunk-RYQCIY6F-D0y50t5B.cjs} +1 -1
  30. package/dist/{chunk-V7JOEXUC-COa9uddv.js → chunk-V7JOEXUC-BNo-X7NC.js} +5 -5
  31. package/dist/{chunk-V7JOEXUC-DIQ8n1AN.cjs → chunk-V7JOEXUC-DXSQM1ea.cjs} +1 -1
  32. package/dist/{chunk-VR4S4FIN-B72PxPVm.js → chunk-VR4S4FIN-BGGITgpQ.js} +3 -3
  33. package/dist/{chunk-VR4S4FIN-Cy-2mZJB.cjs → chunk-VR4S4FIN-Bc567umS.cjs} +1 -1
  34. package/dist/{chunk-XXDRQBXY-DmoUE7-y.cjs → chunk-XXDRQBXY-DOuLpiJ4.cjs} +1 -1
  35. package/dist/{chunk-XXDRQBXY-D2Sgptl-.js → chunk-XXDRQBXY-Drnpl-KK.js} +1 -1
  36. package/dist/{classDiagram-v2-EOCWNBFH-Dah4_VL7.js → classDiagram-OUVF2IWQ-Cneye7qT.js} +2 -2
  37. package/dist/{classDiagram-OUVF2IWQ-GjsCEuKI.cjs → classDiagram-OUVF2IWQ-mmjBEYmN.cjs} +1 -1
  38. package/dist/{classDiagram-OUVF2IWQ-Dah4_VL7.js → classDiagram-v2-EOCWNBFH-Cneye7qT.js} +2 -2
  39. package/dist/{classDiagram-v2-EOCWNBFH-GjsCEuKI.cjs → classDiagram-v2-EOCWNBFH-mmjBEYmN.cjs} +1 -1
  40. package/dist/components/MarkdownRenderer.d.ts +5 -2
  41. package/dist/components/MarkdownViewer.d.ts +3 -2
  42. package/dist/components/NodesRenderer.d.ts +1 -5
  43. package/dist/components/embeds/ClientFrame.d.ts +13 -0
  44. package/dist/components/embeds/LinkCard.d.ts +20 -0
  45. package/dist/components/embeds/ProviderEmbed.d.ts +11 -0
  46. package/dist/components/embeds/embedLabels.d.ts +12 -0
  47. package/dist/components/embeds/frameMessages.d.ts +7 -0
  48. package/dist/components/embeds/markdownEmbeds.d.ts +2 -0
  49. package/dist/components/embeds/requestQueue.d.ts +16 -0
  50. package/dist/components/embeds.d.ts +0 -8
  51. package/dist/components/htmlSanitizer.d.ts +4 -0
  52. package/dist/components/mdastNodes/HTMLBlockContainerNode.d.ts +1 -2
  53. package/dist/components/rawHtml.d.ts +6 -3
  54. package/dist/components/viewerLabels.d.ts +3 -1
  55. package/dist/{cose-bilkent-JH36ORCC-Pi0l3C5o.js → cose-bilkent-JH36ORCC-BQWfGE1G.js} +1 -1
  56. package/dist/{cose-bilkent-JH36ORCC-Vi9pHFwc.cjs → cose-bilkent-JH36ORCC-BaEdcRlX.cjs} +1 -1
  57. package/dist/css/zenn-content.css +0 -40
  58. package/dist/{cynefinDiagram-TSTJHNR4-CIh03JTU.js → cynefinDiagram-TSTJHNR4-CTDDSUz7.js} +47 -47
  59. package/dist/{cynefinDiagram-TSTJHNR4-BQ2k9Z6b.cjs → cynefinDiagram-TSTJHNR4-DPQl09kC.cjs} +1 -1
  60. package/dist/{dagre-VKFMJZFB-cINcPwV3.js → dagre-VKFMJZFB-BMRdJgZy.js} +27 -27
  61. package/dist/{dagre-VKFMJZFB-DI2wSrAy.cjs → dagre-VKFMJZFB-DZ4E-Szs.cjs} +1 -1
  62. package/dist/{diagram-FQU43EPY-BonrBTG_.js → diagram-FQU43EPY-CmEpNw5i.js} +42 -42
  63. package/dist/{diagram-FQU43EPY-B6iUH2l5.cjs → diagram-FQU43EPY-mK9YHIy1.cjs} +1 -1
  64. package/dist/{diagram-G47NLZAW-CY6mWS2R.cjs → diagram-G47NLZAW-BX8Lj_uD.cjs} +1 -1
  65. package/dist/{diagram-G47NLZAW-5N876QQA.js → diagram-G47NLZAW-eJRQFjAg.js} +83 -83
  66. package/dist/{diagram-NH7WQ7WH-x3SaRfd6.cjs → diagram-NH7WQ7WH-3r1_3TrC.cjs} +1 -1
  67. package/dist/{diagram-NH7WQ7WH-LFuTbHxY.js → diagram-NH7WQ7WH-BOOj_4-N.js} +18 -18
  68. package/dist/{diagram-OA4YK3LP-B9mVw8cB.cjs → diagram-OA4YK3LP-CkDJzLOy.cjs} +1 -1
  69. package/dist/{diagram-OA4YK3LP-Cinpbl0B.js → diagram-OA4YK3LP-DnDIHtcW.js} +61 -61
  70. package/dist/{diagram-WEI45ONY-HPCHpafe.js → diagram-WEI45ONY-BHpuPTt-.js} +21 -21
  71. package/dist/{diagram-WEI45ONY-BRYVUhRr.cjs → diagram-WEI45ONY-BpCCo_Ty.cjs} +1 -1
  72. package/dist/{ebnfDiagram-CCIWWBDH-oLOdtH39.js → ebnfDiagram-CCIWWBDH-BDLB-AVF.js} +5 -5
  73. package/dist/{ebnfDiagram-CCIWWBDH-DhFlIi0a.cjs → ebnfDiagram-CCIWWBDH-CfdalXdM.cjs} +1 -1
  74. package/dist/embeds/frames.d.ts +4 -0
  75. package/dist/embeds/linkCards.d.ts +48 -0
  76. package/dist/embeds/placeholder.d.ts +15 -0
  77. package/dist/embeds/providers.d.ts +73 -0
  78. package/dist/embeds/testing.cjs +1 -0
  79. package/dist/embeds/testing.d.ts +7 -0
  80. package/dist/embeds/testing.js +67 -0
  81. package/dist/embeds.cjs +1 -0
  82. package/dist/embeds.d.ts +4 -0
  83. package/dist/embeds.js +73 -0
  84. package/dist/{erDiagram-Q63AITRT-DCCFR1Ai.cjs → erDiagram-Q63AITRT-CviOyQsx.cjs} +1 -1
  85. package/dist/{erDiagram-Q63AITRT-CCtzpK78.js → erDiagram-Q63AITRT-DZlcgZfp.js} +9 -9
  86. package/dist/{extractHeadings-CGJexznE.js → extractHeadings-CtiYkq2z.js} +5482 -5431
  87. package/dist/extractHeadings-D1A8WzVy.cjs +311 -0
  88. package/dist/flowDiagram-23GEKE2U-C_FW0q38.js +7 -0
  89. package/dist/flowDiagram-23GEKE2U-Cb0U58EU.cjs +1 -0
  90. package/dist/{ganttDiagram-NO4QXBWP-C9RPyo4r.cjs → ganttDiagram-NO4QXBWP-CJa-hKPU.cjs} +1 -1
  91. package/dist/{ganttDiagram-NO4QXBWP-Dfi3nINO.js → ganttDiagram-NO4QXBWP-DO0HAbYg.js} +69 -69
  92. package/dist/{gitGraphDiagram-IHSO6WYX-CjAqPhww.js → gitGraphDiagram-IHSO6WYX-CXStgPQs.js} +38 -38
  93. package/dist/{gitGraphDiagram-IHSO6WYX-mT3NNT7T.cjs → gitGraphDiagram-IHSO6WYX-CXcPHbkt.cjs} +1 -1
  94. package/dist/index.cjs +1 -1
  95. package/dist/index.client.cjs +1 -1
  96. package/dist/index.client.d.ts +1 -10
  97. package/dist/index.client.js +41 -49
  98. package/dist/index.js +41 -49
  99. package/dist/index.server.cjs +1 -1
  100. package/dist/index.server.d.ts +1 -2
  101. package/dist/index.server.js +593 -191
  102. package/dist/{infoDiagram-FWYZ7A6U-0Co9BitR.cjs → infoDiagram-FWYZ7A6U-BByoHihL.cjs} +1 -1
  103. package/dist/{infoDiagram-FWYZ7A6U-BXWQ9Vy4.js → infoDiagram-FWYZ7A6U-K0__ySEG.js} +1 -1
  104. package/dist/{ishikawaDiagram-FXEZZL3T-CZp5P1WE.js → ishikawaDiagram-FXEZZL3T-C8QlwQP1.js} +35 -35
  105. package/dist/{ishikawaDiagram-FXEZZL3T-BKqUBw1i.cjs → ishikawaDiagram-FXEZZL3T-DZOMdeTI.cjs} +1 -1
  106. package/dist/{journeyDiagram-5HDEW3XC-DYVTgOOE.js → journeyDiagram-5HDEW3XC-CZ9PFNNN.js} +39 -39
  107. package/dist/{journeyDiagram-5HDEW3XC-CeumPQ1I.cjs → journeyDiagram-5HDEW3XC-RvC77rKo.cjs} +1 -1
  108. package/dist/{kanban-definition-HUTT4EX6-Cg5KnsE9.cjs → kanban-definition-HUTT4EX6-BL1jihGl.cjs} +1 -1
  109. package/dist/{kanban-definition-HUTT4EX6-CnleRy80.js → kanban-definition-HUTT4EX6-Q12F4vHx.js} +123 -123
  110. package/dist/{linear-Cf8i6Rsk.js → linear-LWjATjog.js} +32 -32
  111. package/dist/{linear-qqwu6T0F.cjs → linear-lDvPoqlB.cjs} +1 -1
  112. package/dist/linkCardServer/addressGuard.server.d.ts +25 -0
  113. package/dist/linkCardServer/htmlEncoding.server.d.ts +6 -0
  114. package/dist/linkCardServer/linkCardHandler.server.d.ts +7 -0
  115. package/dist/linkCardServer/metadataCache.server.d.ts +5 -0
  116. package/dist/linkCardServer/pageFetch.server.d.ts +37 -0
  117. package/dist/linkCardServer/pageMetadata.server.d.ts +11 -0
  118. package/dist/linkCards-1JxY8wdm.js +48 -0
  119. package/dist/linkCards-Bjd_vW39.cjs +1 -0
  120. package/dist/markdown.css +1 -1
  121. package/dist/{mindmap-definition-LN4V7U3C-lBin9VOm.js → mindmap-definition-LN4V7U3C-CM1XvwJX.js} +33 -33
  122. package/dist/{mindmap-definition-LN4V7U3C-DfFWeFph.cjs → mindmap-definition-LN4V7U3C-TVYKONRm.cjs} +1 -1
  123. package/dist/{pegDiagram-2B236MQR-BiD21lOj.js → pegDiagram-2B236MQR-Dlrnikqd.js} +5 -5
  124. package/dist/{pegDiagram-2B236MQR-Cf1f-3yi.cjs → pegDiagram-2B236MQR-a9Up0ERY.cjs} +1 -1
  125. package/dist/{pieDiagram-ENE6RG2P-jwhVsZgX.cjs → pieDiagram-ENE6RG2P-ClakmSma.cjs} +1 -1
  126. package/dist/{pieDiagram-ENE6RG2P-BqZb6gaV.js → pieDiagram-ENE6RG2P-WYrEJp8U.js} +58 -58
  127. package/dist/providers-Bxr2Wb-j.cjs +1 -0
  128. package/dist/providers-Drq7MT_K.js +209 -0
  129. package/dist/{quadrantDiagram-ABIIQ3AL-CNe9fRLO.js → quadrantDiagram-ABIIQ3AL-BkgMVDaX.js} +48 -48
  130. package/dist/{quadrantDiagram-ABIIQ3AL-sO_sIA7b.cjs → quadrantDiagram-ABIIQ3AL-Cb01YcqR.cjs} +1 -1
  131. package/dist/{railroadDiagram-RFXS5EU6-Dkc8k3e9.cjs → railroadDiagram-RFXS5EU6-Bob4WSMz.cjs} +1 -1
  132. package/dist/{railroadDiagram-RFXS5EU6-53YTkJrs.js → railroadDiagram-RFXS5EU6-RfCtZGUR.js} +3 -3
  133. package/dist/{requirementDiagram-TGXJPOKE-C0GMlJO4.cjs → requirementDiagram-TGXJPOKE-BP5h9mCj.cjs} +1 -1
  134. package/dist/{requirementDiagram-TGXJPOKE-D8Gz7jzA.js → requirementDiagram-TGXJPOKE-C1yJPMp1.js} +3 -3
  135. package/dist/{sankeyDiagram-HTMAVEWB-CEC0GaCg.js → sankeyDiagram-HTMAVEWB-D5G9Dcmr.js} +67 -67
  136. package/dist/{sankeyDiagram-HTMAVEWB-Bw9J2Ay2.cjs → sankeyDiagram-HTMAVEWB-shq9WK4t.cjs} +1 -1
  137. package/dist/{sequenceDiagram-DBY2YBRQ-CKb4m3I0.js → sequenceDiagram-DBY2YBRQ-8EJMQxiB.js} +47 -47
  138. package/dist/{sequenceDiagram-DBY2YBRQ-BuqPCreB.cjs → sequenceDiagram-DBY2YBRQ-BRxcK5-5.cjs} +1 -1
  139. package/dist/{simple-logger-DZxBpX7v.cjs → simple-logger-9yf7ypyB.cjs} +1 -1
  140. package/dist/{simple-logger-i94ikRyH.js → simple-logger-DwH1YQvq.js} +3 -1
  141. package/dist/{sizeCapture-X5ZJPWSS-Dm3rsxXw.cjs → sizeCapture-X5ZJPWSS-BtBRJO2R.cjs} +1 -1
  142. package/dist/{sizeCapture-X5ZJPWSS-CotnTNAA.js → sizeCapture-X5ZJPWSS-DJG85IuR.js} +1 -1
  143. package/dist/{stateDiagram-2N3HPSRC-GWEIP-C9.js → stateDiagram-2N3HPSRC-CAd-NU-f.js} +22 -22
  144. package/dist/{stateDiagram-2N3HPSRC-5Y-OlePe.cjs → stateDiagram-2N3HPSRC-Czgkv_ey.cjs} +1 -1
  145. package/dist/{stateDiagram-v2-6OUMAXLB-v77D3Qyf.cjs → stateDiagram-v2-6OUMAXLB-BNP5Lx0Y.cjs} +1 -1
  146. package/dist/{stateDiagram-v2-6OUMAXLB-BZFV8swJ.js → stateDiagram-v2-6OUMAXLB-x_Zps-d5.js} +2 -2
  147. package/dist/styles/markdown.css +1 -1
  148. package/dist/{swimlanes-5IMT3BWC-B1hLl8Ph.cjs → swimlanes-5IMT3BWC-CfdkDd3q.cjs} +1 -1
  149. package/dist/{swimlanes-5IMT3BWC-DujAOwAt.js → swimlanes-5IMT3BWC-D8brzzxb.js} +313 -313
  150. package/dist/{swimlanesDiagram-G3AALYLV-B-5z3jDg.js → swimlanesDiagram-G3AALYLV-Bc9-lR2e.js} +2 -2
  151. package/dist/{swimlanesDiagram-G3AALYLV-Cklgsxbg.cjs → swimlanesDiagram-G3AALYLV-dvJpSdGV.cjs} +1 -1
  152. package/dist/{timeline-definition-FHXFAJF6-B2m-aQLI.cjs → timeline-definition-FHXFAJF6-BSqACati.cjs} +1 -1
  153. package/dist/{timeline-definition-FHXFAJF6-Fmzt_lvR.js → timeline-definition-FHXFAJF6-CKW3NJOF.js} +34 -34
  154. package/dist/utils/simple-logger.d.ts +1 -0
  155. package/dist/{vennDiagram-L72KCM5P-rL7UDYbl.cjs → vennDiagram-L72KCM5P-ChKxsFyJ.cjs} +1 -1
  156. package/dist/{vennDiagram-L72KCM5P-zIXK42Cx.js → vennDiagram-L72KCM5P-vPmq984y.js} +92 -92
  157. package/dist/{wardleyDiagram-EHGQE667-pTeeZ0uM.js → wardleyDiagram-EHGQE667-CwN9zwBX.js} +11 -11
  158. package/dist/{wardleyDiagram-EHGQE667-CNLieFen.cjs → wardleyDiagram-EHGQE667-CxzlR7km.cjs} +1 -1
  159. package/dist/{xychartDiagram-FW5EYKEG-B6UdSSVZ.js → xychartDiagram-FW5EYKEG-DBSKTLuc.js} +37 -37
  160. package/dist/{xychartDiagram-FW5EYKEG-CD5ik4HK.cjs → xychartDiagram-FW5EYKEG-wJqYRInm.cjs} +1 -1
  161. package/package.json +25 -9
  162. package/dist/arc-DH6k2JIR.js +0 -83
  163. package/dist/channel-DoH_tsOQ.js +0 -5
  164. package/dist/components/mdastNodes/BlockLinkNode.d.ts +0 -5
  165. package/dist/components/mdastNodes/RichLinkCard.d.ts +0 -8
  166. package/dist/components/mdastNodes/TwitterEmbedNode.d.ts +0 -25
  167. package/dist/components/mdastNodes/YouTubeEmbedNode.d.ts +0 -6
  168. package/dist/extractHeadings-lDHB2sBb.cjs +0 -310
  169. package/dist/flowDiagram-23GEKE2U-ORytlvmX.js +0 -7
  170. package/dist/flowDiagram-23GEKE2U-h2WZTCCn.cjs +0 -1
  171. package/dist/plugins/createIntentTweetLink.d.ts +0 -10
  172. package/dist/plugins/extractYouTubeVideoId.d.ts +0 -5
  173. package/dist/plugins/fetchSiteMetadata.d.ts +0 -9
  174. package/dist/plugins/remark-auto-heading-id.d.ts +0 -2
  175. package/dist/plugins/remark-block-link-metadata.d.ts +0 -23
  176. package/dist/plugins/remark-block-link.d.ts +0 -12
  177. package/dist/plugins/remark-twitter-embed.d.ts +0 -20
  178. package/dist/plugins/remark-youtube-embed.d.ts +0 -14
  179. package/dist/utils/getFaviconUrl.d.ts +0 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@ Advanced Markdown renderer for React with support for various extensions and cus
6
6
 
7
7
  - **Rich Markdown Support**: Full support for CommonMark, GitHub Flavored Markdown (GFM), and LaTeX Math expressions.
8
8
  - **Offloaded Parser**: CPU-heavy Markdown parsing and AST transformation are offloaded to a high-performance Go API server running `goldmark` and `ConnectRPC`.
9
- - **Custom Extensions & Plugins**: GitHub alerts, Zenn-style directives (`:::message` / `:::details`), single-line display math, and pluggable subscript, superscript, mark highlights, table cell merges and embeds (YouTube, Twitter).
9
+ - **Custom Extensions & Plugins**: GitHub alerts, Zenn-style directives (`:::message` / `:::details`), single-line display math, and pluggable subscript, superscript, mark highlights, table cell merges, and opt-in embeds: posts and videos of ten SNS and video providers as script-free cross-origin frames, and link cards from the host's metadata endpoint.
10
10
  - **Dynamic Configuration**: Easily enable or disable custom extensions dynamically using parse options.
11
11
  - **Syntax Highlighting**: Code blocks with Shiki syntax highlighting.
12
12
  - **TypeScript**: Full TypeScript support with comprehensive type definitions.
@@ -71,9 +71,13 @@ export MARKDOWN_API_BINARY=/path/to/markdown-api
71
71
 
72
72
  ```tsx
73
73
  import { MarkdownRenderer } from "@aiquants/markdown"
74
+ import type { MarkdownEmbeds } from "@aiquants/markdown/embeds"
74
75
  import { parseMarkdown } from "@aiquants/markdown/server"
75
76
  import "@aiquants/markdown/styles/markdown.css"
76
77
 
78
+ // A module constant: the providers whose posts embed, and the host's link-card endpoint (see Embeds)
79
+ const EMBEDS: MarkdownEmbeds = { providers: ["youtube", "x"], linkCardEndpoint: "/api/site_metadata" }
80
+
77
81
  const MyComponent = async () => {
78
82
  const markdownText = `# Hello World
79
83
 
@@ -103,7 +107,7 @@ console.log("Hello, World!")
103
107
  <MarkdownRenderer
104
108
  htmlContent={parseResult.htmlContent}
105
109
  theme="light"
106
- iFrameAllowedDomains={["www.youtube.com"]}
110
+ embeds={EMBEDS}
107
111
  />
108
112
  )
109
113
  }
@@ -133,7 +137,7 @@ const parseResult = await parseMarkdown(
133
137
  enableSuperscript: true, // Enable superscript
134
138
  enableMark: false, // Disable highlight mark
135
139
  enableTableMerge: true, // Enable table merges (> and ^)
136
- enableEmbeds: true, // Enable YouTube / Twitter embeds
140
+ enableEmbeds: true, // Mark a paragraph holding one web link (data-embed-url)
137
141
  enableZennDirectives: true // Enable :::message / :::details
138
142
  }
139
143
  )
@@ -187,28 +191,162 @@ Every renderer of the package — `MarkdownRenderer` (and so `MarkdownViewer`) f
187
191
  - **Kept**: the elements Markdown produces and common text and structure elements (`a`, `img`, `p`, `div`, `span`, headings, lists, tables, `blockquote`, `pre`, `code`, `details`, `summary`, `aside`, `section`, `figure`, `sup`, `sub`, `mark`, `video`, `audio`, `source` …) and, for icons, an `svg` with `g` and `path` inside it (anything else inside an `svg` is removed).
188
192
  - **Unwrapped** (the element is dropped, its children render): every other element — `form`, `button`, `textarea`, `label`, `font`, `center`, custom elements and the like — so its text survives.
189
193
  - **Attributes**: `title`, `lang`, `dir`, `role`, `hidden`, `translate`, `aria-*`, a reduced `class`, `id` and `style`, and each element's own (`href`, `src`, `alt`, `width`, `height`, `colspan`, `rowspan`, `align`, `open`, `start` …; a link keeps `name`, the fragment target of older documents).
190
- The `data-*` attributes the renderers read survive on their element only: `data-link-kind`, `data-doc-path`, `data-doc-exists`, `data-footnote-ref` and `data-footnote-backref` on links, `data-asset-src` and `data-asset-exists` on images, `data-footnotes` on sections, and the embed placeholders' `data-plugin-type`, `data-video-id`, `data-tweet-id` and `data-url` on `div`. Event handlers, `target`, `rel`, `name` (except on links), `srcset`, `usemap`, `ping`, `formaction`, `xlink:href`, `tabindex` and every other attribute are dropped.
194
+ The `data-*` attributes the renderers read survive on their element only: `data-link-kind`, `data-doc-path`, `data-doc-exists`, `data-footnote-ref` and `data-footnote-backref` on links, `data-asset-src` and `data-asset-exists` on images, and `data-footnotes` on sections. A paragraph's `data-embed-url` never renders: when it is a safe URL the paragraph becomes an embed placeholder (see [Embeds](#embeds)); the 5.x `div` markers (`data-plugin-type`, `data-video-id`, `data-tweet-id`, `data-url`) are dropped.
195
+ Event handlers, `target`, `rel`, `name` (except on links), `srcset`, `usemap`, `ping`, `formaction`, `xlink:href`, `tabindex` and every other attribute are dropped.
191
196
  - **Attribute names** are read as an HTML parser stores them: in lower case, and of names that differ only in case the first one written. Kept attributes carry lower-case names, so `<p STYLE="text-align:center">` or `<a HREF="https://…">` renders the same on the server (where html-react-parser keeps the author's case) and in the browser, and hydrates without a mismatch.
192
197
  - **`class`** keeps only the package's own tokens: the converter's markup (`markdown-alert`, `markdown-alert-{note,tip,important,warning,caution}`, `markdown-alert-title`, `octicon`, `octicon-{info,light-bulb,report,alert,stop}`, `mr-2`, `message`, `alert`, `info`, `warning`, `message-symbol`, `message-content`, `markdown-details`, `markdown-details-content`, `footnotes`, `footnote-ref`, `footnote-backref`, `math`, `inline`, `display`) and a code block's language (`language-*`).
193
198
  An author's tokens and utility classes (`fixed inset-0 z-50` …) are dropped, so a document can neither restyle the page nor lay an element over it; a link's `className` (also the one `linkComponent` receives) holds only the kept tokens and the package's link classes. Tables carry their alignment in `align` and `style`, not in classes.
194
199
  - **`id`** survives only on a heading (the table of contents, in-document links and a host's fragment navigation name headings by it), on the converter's footnotes (`fnref:N` / `fnrefK:N` on a reference's `sup`, `fn:N` on a note's `li`) and in the user-content namespace (`user-content-…`, the prefix GitHub-style footnotes use; write raw HTML fragment targets with it, or as `<a name="…">`).
195
- Every other element loses its id, so a document cannot take a name the page or a script looks up (`getElementById`, the named properties of `window`). A heading keeps whatever id the document gives it: a host should not look up its own elements by an id a document could also give a heading (prefer refs), and the package's own lookups check what they find (see [Embeds](#embeds)).
200
+ Every other element loses its id, so a document cannot take a name the page or a script looks up (`getElementById`, the named properties of `window`). A heading keeps whatever id the document gives it: a host should not look up its own elements by an id a document could also give a heading (prefer refs).
196
201
  - **Host sanitizers**: a host that sanitizes the HTML before the renderer (as `@aiquants/markdown-explorer` does with DOMPurify) gets the intersection of both allow lists; DOMPurify keeps `class` and `id` by default, so on the page the rules above are the ones that apply.
197
- - **URLs** (`href`, `src`, `cite`, `poster`, `data-url`): `http:`, `https:`, `mailto:` and `tel:` in any letter case, and references without a scheme. `javascript:`, `data:` and every other scheme are dropped, however they are spelled (entities, spaces, tabs, control characters). The mdast `LinkNode` and `ImageNode`, the block link card `RichLinkCard` (its `href`, and the `url` and `image` of the site metadata it shows) and the table of contents' entries follow the same rule.
202
+ - **URLs** (`href`, `src`, `cite`, `poster`, `data-embed-url`): `http:`, `https:`, `mailto:` and `tel:` in any letter case, and references without a scheme. `javascript:`, `data:` and every other scheme are dropped, however they are spelled (entities, spaces, tabs, control characters). The mdast `LinkNode` and `ImageNode`, link cards (their link, image and icon) and the table of contents' entries follow the same rule.
198
203
  - **`style`** keeps only its last valid `text-align` declaration (the alignment of table cells); positioning, `url()` and every other declaration are dropped. An SVG `fill` that references anything (`url(…)`) is dropped.
199
204
 
200
205
  ### Embeds
201
206
 
202
- - `iFrameAllowedDomains` lists the hostnames an iframe may load from: the `src` must be `https:`, without a user name, a password or an explicit port, and its hostname must match an entry exactly. Every other iframe is removed (fail closed), including an iframe without `src` (`srcdoc`). An allowed iframe is rendered from validated values and keeps only `title`, `width`, `height`, `allow`, `allowfullscreen`, `sandbox`, `referrerpolicy`, `loading`, `frameborder` and `scrolling` (no `id` or `class`: the package's markup gives an iframe neither).
203
- `allow` keeps each feature's name without its allowlist, so a feature applies to the embedded origin only (`camera *; microphone 'self' https://x.example` becomes `camera; microphone`); `referrerpolicy` is kept only as `no-referrer`, `origin`, `same-origin`, `strict-origin` or `strict-origin-when-cross-origin`.
204
- - An iframe plugin (`data-plugin-type="dify"`) receives the validated iframe as structured props, never as an HTML string. The Dify plugin acts only on messages its own frame's window posts from an allowed origin, and ignores a message that is not an object with a string `type`.
205
- - A YouTube embed loads the player only when `www.youtube.com` is in `iFrameAllowedDomains`; otherwise it renders a link to the video's watch page.
206
- - A tweet renders as a link to its address unless the host sets `allowTwitterWidgetScript` (on `MarkdownRenderer` and `MarkdownViewer`), which lets the page load and run `https://platform.twitter.com/widgets.js`. The widgets are taken from that script only: an existing copy is found as a `script` element with that `src` (never by the `twitter-wjs` id), and `window.twttr.widgets` is used only when its `createTweet` is a function, so an element a document names `twitter-wjs` or `twttr` cannot block or impersonate it.
207
+ Embeds are opt in. `MarkdownRenderer` and `MarkdownViewer` take `embeds={{ providers, linkCardEndpoint, linkCardSize }}` (type `MarkdownEmbeds` from `@aiquants/markdown/embeds`; `linkCardSize` is optional); the default `NO_EMBEDS` renders every link alone in a paragraph as that paragraph. Pass the value from a module constant (the viewer also compares it by value). An unknown provider id, a bad endpoint or an unknown card size throws a `RangeError` at render.
207
208
 
208
209
  ```tsx
209
- <MarkdownViewer {...props} iFrameAllowedDomains={["www.youtube.com"]} allowTwitterWidgetScript={false} />
210
+ import type { MarkdownEmbeds } from "@aiquants/markdown/embeds"
211
+
212
+ const EMBEDS: MarkdownEmbeds = { providers: ["youtube", "x", "instagram", "bluesky"], linkCardEndpoint: "/api/link-card", linkCardSize: "banner" }
213
+
214
+ <MarkdownViewer {...props} embeds={EMBEDS} iFrameAllowedDomains={["www.youtube.com"]} />
210
215
  ```
211
216
 
217
+ **What a link alone in a paragraph becomes.** The converter marks a paragraph whose only element is one link to an absolute `http(s)` URL without a user name or password (besides white space, U+00A0 and U+3000) with `data-embed-url`, and keeps the paragraph and the link. The renderer then decides, in this order:
218
+
219
+ 1. The marked link must be the paragraph's only link, with the same sanitized `href` (`placeholderLinkOf`); otherwise the paragraph renders as written.
220
+ 2. The fallback is a link card when `linkCardEndpoint` is set and the link's text is the URL itself (`isBareLink`), otherwise the paragraph.
221
+ 3. An enabled provider that recognizes the URL embeds its post or video, whatever the link text says, with the paragraph's own link as the caption; otherwise the fallback renders.
222
+
223
+ The link is always kept: under the frame, as the card, or as the paragraph.
224
+
225
+ | Provider id | URLs it recognizes | Frame origin |
226
+ | --- | --- | --- |
227
+ | `youtube` | `www.youtube.com`, `youtube.com`, `m.youtube.com`: `/watch?v=` (any position; `si`, `list`, `feature` ignored), `/shorts/ID`, `/live/ID`, `/embed/ID`; `youtu.be/ID`; `www.youtube-nocookie.com/embed/ID` | `https://www.youtube-nocookie.com` |
228
+ | `x` | `x.com`, `twitter.com` (`www.`, `mobile.`): `/USER/status/ID` (with `/photo/N` or `/video/N`), `/i/web/status/ID` | `https://platform.twitter.com` |
229
+ | `instagram` | `www.instagram.com`, `instagram.com`, `instagr.am`: `/p/CODE`, `/reel/CODE`, `/USER/p/CODE`, `/USER/reel/CODE` | `https://www.instagram.com` |
230
+ | `threads` | `www.threads.com`, `threads.com`, `www.threads.net`, `threads.net`: `/@USER/post/CODE` | `https://www.threads.com` |
231
+ | `tiktok` | `www.tiktok.com`, `tiktok.com`: `/@USER/video/ID`, `/player/v1/ID`, `/embed/v2/ID` | `https://www.tiktok.com` |
232
+ | `bluesky` | `bsky.app/profile/ACTOR/post/RKEY` (a DID, or a handle looked up at `https://public.api.bsky.app`) | `https://embed.bsky.app` |
233
+ | `linkedin` | `www.linkedin.com`, `linkedin.com`: `/feed/update/URN`, `/embed/feed/update/URN`, `/posts/…-activity-ID-…` | `https://www.linkedin.com` |
234
+ | `reddit` | `www.reddit.com`, `reddit.com`, `old.reddit.com`, `new.reddit.com`: `/r/SUB/comments/ID[/SLUG]` | `https://embed.reddit.com` |
235
+ | `note` | `note.com/USER/n/KEY` | `https://note.com` |
236
+ | `niconico` | `www.nicovideo.jp`, `nicovideo.jp`: `/watch/ID`; `nico.ms/ID` | `https://embed.nicovideo.jp` |
237
+
238
+ Every host must match exactly (look-alike hosts, user info, ports and URLs carrying a provider URL in their query never match), and each path segment must match its own grammar. Short links without an id, Facebook, Pinterest, TikTok photo posts and LinkedIn articles have no provider: as bare URLs they become cards.
239
+
240
+ **Frames.** No provider script ever runs in the page: posts and videos are cross-origin iframes with the registry's attributes (an `allow` within the six shared features, a `sandbox`, a referrer policy, `loading="lazy"`), titled `"<provider>: <label>"`. Every iframe — provider frames, iframes written in documents and the Dify plugin — mounts only on the client after hydration, and **never when its origin is the page's own origin**, whatever the host allows.
241
+ The server HTML holds the link and a reserved box (`width: 100%` up to the provider's maximum, its aspect ratio or height, never a CSS `min-width`), hidden for readers without JavaScript. A frame whose box is narrower than the provider's minimum (YouTube 356 px, Shorts 200 px, X 250 px, Instagram 326 px, Threads 281 px, Bluesky 300 px) shows the fallback instead, and mounts again when the pane widens. X follows the primary subtag of the page's `<html lang>`; X, Threads and Bluesky follow `theme`.
242
+ Frames that report their height (X, Instagram, Threads, Bluesky, Reddit, note) are sized through one `message` listener that accepts only the frame's own window from its exact provider origin.
243
+
244
+ **Link cards.** A card asks `GET <linkCardEndpoint>?url=<href>` (same-origin credentials, `redirect: "error"`) once an IntersectionObserver rooted at the viewer's scroller sees it, through a page-wide queue (two requests at once, 256 answers kept, aborted when no card needs the answer).
245
+ It links to the written `href` (never `og:url`), takes the host name from the `href`, and shows the image and the icon only when the endpoint gives absolute `http(s)` URLs of at most 2048 characters (`LINK_CARD_URL_LIMIT`) that are not on the page's own host, whatever the scheme or port (a browser loads an `http:` image of an `https:` page over `https:`, and cookies ignore the port), with no referrer. The package asks no favicon service.
246
+
247
+ `linkCardSize` picks the size of every card:
248
+
249
+ | `linkCardSize` | The card |
250
+ | --- | --- |
251
+ | `"banner"` (default) | Image, title (two lines), description (one line) and `site · host`, 7.5rem tall, as wide as the column up to 37.5rem (600px) and left-aligned. The image is 1.91:1 from a card width of 32rem, square from 22rem, hidden below. |
252
+ | `"full"` | Image, title and description (two lines each) and `site · host`, 8.5rem tall, across the column. The image is 1.91:1 from 40rem, square from 22rem, hidden below. |
253
+ | `"compact"` | The favicon (or a neutral link icon), the title on one line and `site · host`, 3.75rem tall, as wide as its text up to 37.5rem. No image and no description. |
254
+
255
+ A pending card has the height of the chosen size, so an answer never shifts the page. A link the endpoint gives no card (a `204 No Content` with a reason, below) becomes a compact card whatever the size: a leading icon — a building for `private-network`, a globe otherwise — then the host name and the URL, marked `data-link-card="compact"` and `data-link-card-reason="<reason>"`. It may shrink a pending banner or full card; Chromium's scroll anchoring keeps the visible content in place when that happens above the viewport. An error answer renders the paragraph.
256
+ Every link that gets no full card writes one `console.info` line naming the link and the reason, for example `[markdown] No link card for <href> (private-network)`; an error answer names its cause instead — `HTTP <status>` for another status, `HTTP 204 without a known skip reason` for a 204 whose `X-Link-Card-Skip` is missing or unknown, `HTTP 200, not JSON` or `HTTP 200, not a JSON object` for a 200 that is not a JSON object, `network error` for a failed request — and a card that unmounts before its answer writes nothing.
257
+ The card's look — light and dark (`data-theme`), hover, keyboard focus ring — comes from `styles/markdown.css`, outside every cascade layer, so the GitHub and Zenn display styles cannot override it; every size shares that one frame.
258
+
259
+ The endpoint contract:
260
+
261
+ - `GET <endpoint>?url=<href>` answers JSON `{ title?, description?, site_name?, image?, icon? }`; other fields (`url` included) are ignored.
262
+ - A link that gets no card is answered `204 No Content` with the reason in `X-Link-Card-Skip` (`LINK_CARD_SKIP_HEADER`): `private-network`, `forbidden-url`, `unreachable` or `not-html` (`LINK_CARD_SKIP_REASONS`, read with `linkCardSkipReasonOf`). A 204 without one of them is an error answer.
263
+ - It must not set cookies or refresh credentials: the request runs in the background and can be in flight during a sign-out.
264
+ - A redirect, any other non-OK status or a non-JSON answer is an error answer: no card, the paragraph.
265
+ - `createLinkCardHandler` from `@aiquants/markdown/server` implements this contract (see [Link-card endpoint](#link-card-endpoint)).
266
+
267
+ **Iframes written in documents.** `iFrameAllowedDomains` lists the hostnames such an iframe may load from: the `src` must be `https:`, without a user name, a password or an explicit port, and its hostname must match an entry exactly. Every other iframe is removed (fail closed), including an iframe without `src` (`srcdoc`). An allowed iframe is rendered from validated values and keeps only `title`, `width`, `height`, `allow`, `allowfullscreen`, `sandbox`, `referrerpolicy`, `loading`, `frameborder` and `scrolling` (no `id` or `class`).
268
+ An iframe without a `title` gets `"<embeddedFrame>: <host>"` (for example `Embedded content: www.youtube.com`), marked with the built-in label's language.
269
+ `allow` keeps only the six shared frame features — `autoplay`, `clipboard-write`, `encrypted-media`, `fullscreen`, `picture-in-picture`, `web-share` — each without its allowlist (`camera; microphone; autoplay` becomes `autoplay`); `referrerpolicy` is kept only as `no-referrer`, `origin`, `same-origin`, `strict-origin` or `strict-origin-when-cross-origin`. Until it mounts, and for good when its origin is the page's, the iframe renders as `<span data-embed-frame="pending|refused">` holding a link to its `src`.
270
+ An iframe inside a link never mounts and stays that text, without a nested link: interactive content may not sit inside an `<a>`, and activating the link would follow the author's URL instead of the frame.
271
+ An iframe plugin (`data-plugin-type="dify"`) receives the validated iframe as structured props, never as an HTML string. The Dify plugin acts only on messages its own frame's window posts from an allowed origin, and ignores a message that is not an object with a string `type`.
272
+
273
+ **Labels.** `embedLabels` (`embeddedVideo`, `embeddedPost`, `embeddedFrame`; built-in "embedded video", "embedded post", "Embedded content", in `DEFAULT_MARKDOWN_EMBED_LABELS`) name the frames; `MarkdownViewer` takes the same keys in `labels`.
274
+
275
+ **DOM hooks.** `figure[data-embed-provider][data-embed-state="pending|frame"]` with its box `[data-embed-box]`, the width probe `[data-embed-measure]`, `span[data-embed-frame]` for iframes written in documents, and `a[data-link-card="pending|ready|compact"][data-link-card-size="compact|banner|full"]` (a compact card of a skipped link also carries `data-link-card-reason`).
276
+
277
+ **Content Security Policy.** Put `embedFrameHostsOf(embeds.providers)` and the hosts of `iFrameAllowedDomains` in `frame-src`, `embedConnectOriginsOf(embeds.providers)` in `connect-src` (Bluesky handles), and allow `https:` images in `img-src` for card images and icons. Provider frames may open popups that escape their sandbox, so a host that enables providers should send `Cross-Origin-Opener-Policy: same-origin`.
278
+
279
+ `@aiquants/markdown/embeds` holds the placeholder contract, the frame policy, the provider registry and the card contract without React, for host sanitizers, configuration and CSP. `@aiquants/markdown/embeds/testing` holds captured provider messages and `embedProviderStubPage(url)` for offline end-to-end tests.
280
+
281
+ ### Link-card endpoint
282
+
283
+ `createLinkCardHandler(options)` from `@aiquants/markdown/server` (Node only) is the card endpoint as a standard handler, `(request: Request) => Promise<Response>`. It reads only the request's method and its `url` search parameter, never its path, so mount it on any path and pass that path as `embeds.linkCardEndpoint`. Create it once, at module scope: each handler owns its cache and its connection pool. It uses `undici` and `htmlparser2`, which no client entry loads (`@aiquants/markdown/embeds` stays free of React and Node).
284
+
285
+ A React Router resource route (`app/routes/api.link-card.ts`, for example):
286
+
287
+ ```ts
288
+ import { createLinkCardHandler } from "@aiquants/markdown/server"
289
+ import type { LoaderFunctionArgs } from "react-router"
290
+ import { readSignedInReader } from "~/session.server" // the host's read-only session lookup
291
+
292
+ const linkCards = createLinkCardHandler({ authorize: async (request) => (await readSignedInReader(request)) !== undefined })
293
+
294
+ export const loader = ({ request }: LoaderFunctionArgs) => linkCards(request)
295
+ ```
296
+
297
+ Any server whose handlers take a `Request` and return a `Response`:
298
+
299
+ ```ts
300
+ import { createLinkCardHandler } from "@aiquants/markdown/server"
301
+
302
+ const linkCards = createLinkCardHandler({ authorize: () => true }) // a public site: every reader may ask
303
+
304
+ export const handle = (request: Request): Promise<Response> =>
305
+ new URL(request.url).pathname === "/api/link-card" ? linkCards(request) : Promise.resolve(new Response(null, { status: 404 }))
306
+ ```
307
+
308
+ The handler can also be called from inside another loader, for example a data route that dispatches several operations: it answers the request it is given.
309
+
310
+ **Options.** `authorize: (request) => boolean | Promise<boolean>` is the only option, and it is required. Anything but `true` answers 401 before anything is fetched; a host that serves everyone passes `() => true`. It must only read the request — no cookie writes, no token refresh — because cards ask in the background and a request can still be in flight while the reader signs out. A rejection propagates to the host. `createLinkCardHandler` throws a `TypeError` when `authorize` is not a function and for any other option.
311
+
312
+ **Answers.** Every answer carries `X-Content-Type-Options: nosniff` and never `Set-Cookie`; a `HEAD` answer has the `GET` answer's status and headers and no body.
313
+
314
+ | Request | Status | Body | `Cache-Control` |
315
+ | --- | --- | --- | --- |
316
+ | A method other than `GET` and `HEAD` | 405 (with `Allow: GET, HEAD`) | `{"error":"Method Not Allowed"}` | `no-store` |
317
+ | `authorize` does not return `true` | 401 | `{"error":"Unauthorized"}` | `no-store` |
318
+ | No or an empty `url` | 400 | `{"error":"Missing 'url' parameter"}` | `no-store` |
319
+ | A link that gets no card | 204 (with `X-Link-Card-Skip: <reason>`) | none | per reason, below |
320
+ | An unexpected exception | 500 | `{"error":"Internal Server Error"}` | `no-store` |
321
+ | A card | 200 | `{ title?, description?, site_name?, image?, icon? }` (`LinkCardResponseBody`) | `private, max-age=1800` |
322
+
323
+ The JSON answers are `application/json; charset=utf-8`. The reasons of a 204:
324
+
325
+ | Reason | When | `Cache-Control` |
326
+ | --- | --- | --- |
327
+ | `private-network` | The host is internal by name or by IP literal, it resolves to or connects to a private or special-use address, or a redirect leads to such a host | `private, max-age=1800` |
328
+ | `forbidden-url` | The URL or a redirect target is not `http(s)`, carries a user name or password, or does not parse | `private, max-age=1800` |
329
+ | `unreachable` | A network error, a timeout, a non-OK answer of the linked site, too many redirects or an unparsable `Location` | `private, max-age=300` |
330
+ | `not-html` | The page's `Content-Type` is missing or not HTML | `private, max-age=300` |
331
+
332
+ **Internal addresses never become cards**, in any environment; there is no switch. Internal is decided by address, never by the company's domain:
333
+
334
+ - a loopback, private, link-local, CGNAT, multicast, documentation, benchmarking, discard-only or reserved IPv4 or IPv6 address (every IANA special-purpose range that is not globally reachable) written as the host, in every form the URL parser normalizes (`2130706433`, `0x7f.1`, `127.1`, `[::ffff:127.0.0.1]`, and the NAT64, 6to4, Teredo and IPv4-translated forms);
335
+ - an internal name: `localhost`, `*.localhost`, a single-label name, one without a dot (the server's DNS search list would complete it to an internal name), and names under `.local`, `.internal` and `.home.arpa`, with or without a trailing dot;
336
+ - a name that resolves to an internal address: every connection resolves through a lookup that refuses a name when any of its A or AAAA answers is private, and a `net.BlockList` (honoured by `net.connect` from Node 22.13, which `engines` requires) checks the very address connected to — for `https` too, whose TLS handshake runs over a TCP socket opened with the block list — so DNS rebinding cannot slip past;
337
+ - every redirect hop, checked again before it is followed.
338
+
339
+ **What the card shows.** The title is the last `og:title`, falling back to the first `<title>` of the document's head when that `og:title` is blank; the description is the last `og:description` or `<meta name="description">`; the site name the last `og:site_name`; the image the last `og:image`; the icon the first `<link rel>` holding the token `icon` (no `/favicon.ico` is guessed). A later blank `<meta>` clears an earlier one.
340
+ Only the head is read: parsing stops at the first body content, skips the content of a `<noscript>`, `<template>` or `<noframes>` in the head as a browser with scripting does, and stops at an element nesting deeper than 256, after 4096 start tags and at a tag with more than 64 attributes, so a hostile page costs time and memory linear in the bytes read. Text is trimmed and cut at the card's limits (title 300, description 1000, site name 200 code points), so `linkCardMetadataOf` reads every body back unchanged.
341
+ The image and the icon are resolved against the first `<base href>` — itself resolved against the page's URL, as a browser does, and used when it is `http(s)` and at most 2048 characters — else against the page's URL after redirects, and kept only when they are at most 2048 characters (`LINK_CARD_URL_LIMIT`) and the URL check accepts them.
342
+ Only `text/html` and `application/xhtml+xml` answers are read, decoded by WHATWG encoding sniffing: a byte order mark, then the `charset` of `Content-Type`, then a `<meta charset>` or `<meta http-equiv="Content-Type">` in the first 1024 bytes, else UTF-8 (Shift_JIS, EUC-JP, ISO-2022-JP and every other label `TextDecoder` knows; an unknown label is ignored).
343
+
344
+ **Limits.** 5 s per page for the headers, the body and its parsing; 5 redirects, each followed by hand and checked again; 64 KiB of response headers; at most the first 8 MiB of the body, read as a stream and parsed as it arrives, yielding to the event loop between chunks, and the download stops once the head has been read.
345
+ DNS goes through c-ares, never through `getaddrinfo` on libuv's threadpool, so a name whose DNS never answers holds none of the threads the host's `fs`, `zlib` and `crypto` share: A and AAAA queries to the DNS servers of the system's resolver configuration (the hosts file is not read), 1 s for the first attempt and 2 attempts per server; a name connects over IPv4 when it has an A record, else over IPv6.
346
+ Each handler caches cards per URL for 1 hour and skips as long as their `Cache-Control`, 1000 URLs at most; the key is the URL as the URL parser serializes it without its fragment, so spellings of one URL share one entry, and concurrent requests for one URL share one fetch.
347
+
348
+ **Logs.** The handler logs through the package's logger, never with a URL's user name or password (a refused URL that is not `http(s)` is logged by its scheme alone): policy refusals (`private-network`, `forbidden-url`) at info, network errors, unparsable `Location`s, too many redirects and unexpected exceptions at error, and a body cut at the size limit at warn.
349
+
212
350
  ### Links
213
351
 
214
352
  - A link is external when its `href` names a scheme in any case (`https:`, `HTTPS:`, `mailto:`) or another host (`//host/…`, `/\host/…`), whatever its `data-link-kind` says, and when `data-link-kind` is `external`. An external link opens in a new tab with `rel="noreferrer"` and is never handed to `linkComponent`. The author's `target` and `rel` never survive.
@@ -279,7 +417,8 @@ An open, unpinned popover closes on Escape while the focus is inside its menu, r
279
417
 
280
418
  Activating an entry closes an unpinned table of contents, reveals the heading if it is hidden, scrolls to it (instantly under `prefers-reduced-motion: reduce`) and moves the focus to it (adding `tabindex="-1"` when the heading has none), so the next Tab continues from that section. Pass `onHeadingSelect(heading, event)` to take part: return `false` to let the viewer reveal, scroll and focus after your own work (writing the URL hash, say), or `true` when you have handled the navigation yourself.
281
419
 
282
- An entry links to `#` followed by the heading's id. Where the page's URL does not address the document (several documents side by side, a document chosen by state), pass `headingHref(heading)` (`MarkdownHeadingHrefResolver`) to give each entry the heading's own address, so copying the entry's address or opening it in a new tab with a middle click reaches the heading; pass a stable function, since the table of contents is built again when it changes.
420
+ The entry of a heading without an id (`# ★`, whose text keeps no character) is its text alone, not a link: no fragment names that heading, so neither `headingHref` nor `onHeadingSelect` is called for it.
421
+ Any other entry links to `#` followed by the heading's id. Where the page's URL does not address the document (several documents side by side, a document chosen by state), pass `headingHref(heading)` (`MarkdownHeadingHrefResolver`) to give each entry the heading's own address, so copying the entry's address or opening it in a new tab with a middle click reaches the heading; pass a stable function, since the table of contents is built again when it changes.
283
422
  The href goes through the package's URL rule (an entry with a refused href has none), and activating an entry still runs `onHeadingSelect` and the viewer's navigation. A Ctrl-, Cmd- or Shift-click is an activation, so return `true` from `onHeadingSelect` when a modifier key is down to let the browser open the address instead.
284
423
 
285
424
  The viewer stops the link's own navigation, so it reveals the heading the way a browser reveals the fragment it navigates to:
@@ -313,7 +452,7 @@ const headingHref: MarkdownHeadingHrefResolver = (heading) => `/docs/guide.md#${
313
452
 
314
453
  Every name and text the viewer draws comes from `labels` (`MarkdownViewerLabels`), including the table-of-contents and alignment controls and, through the `mermaid*` keys, the controls of each Mermaid diagram (`MermaidDiagram` takes `MermaidDiagramLabels` as `labels`).
315
454
  A label you pass carries no language of its own and is read in your page's language.
316
- A built-in label marks the element that bears it with its own language: the status texts and style controls are English (`lang="en"`), the table-of-contents, alignment and Mermaid menu controls are Japanese (`lang="ja"`), and the Mermaid zoom buttons are English. `DEFAULT_MARKDOWN_VIEWER_LABELS` and `DEFAULT_MERMAID_DIAGRAM_LABELS` hold the built-in texts.
455
+ A built-in label marks the element that bears it with its own language: the status texts and style controls are English (`lang="en"`), the table-of-contents, alignment and Mermaid menu controls are Japanese (`lang="ja"`), and the Mermaid zoom buttons are English. The embed labels (`embeddedVideo`, `embeddedPost`, `embeddedFrame`) are English. `DEFAULT_MARKDOWN_VIEWER_LABELS`, `DEFAULT_MERMAID_DIAGRAM_LABELS` and `DEFAULT_MARKDOWN_EMBED_LABELS` hold the built-in texts.
317
456
 
318
457
  ### Settings without React
319
458
 
@@ -327,6 +466,29 @@ import { DEFAULT_MARKDOWN_STYLES, toContentAlign } from "@aiquants/markdown/view
327
466
 
328
467
  The parser supports the following extended markdown syntax in addition to CommonMark and GFM.
329
468
 
469
+ ### Heading ids
470
+
471
+ The converter gives every heading its id; `MarkdownViewer` and `MarkdownRenderer` render the ids of the HTML and assign none.
472
+
473
+ - **Written id**: end the heading's last line with `{#id}`, or with `{id="…"}` when the id holds a space or ASCII punctuation other than `_`, `-`, `:` and `.` (a `}`, say). `## 概要 {#overview}` gets the id `overview`.
474
+ - It is kept exactly as written, in any script.
475
+ - No automatic id takes it, wherever the heading is.
476
+ - When two headings write the same id, both keep it and a fragment names the first.
477
+ - An id attribute that names nothing — empty (`{#}`, `{id=""}`) or not a string (`{id=123}`) — is removed, and the heading gets its automatic id.
478
+ - **Automatic id**: GitHub's, as github-slugger 2.0.0 makes it from the heading's text:
479
+ - lower-cased;
480
+ - punctuation and symbols removed;
481
+ - letters, marks, digits, `_` and `-` of every script kept;
482
+ - each space becomes `-`;
483
+ - a repeated id gets `-1`, `-2`, ….
484
+ - Examples: `概要` → `概要`, `API の使い方` → `api-の使い方`, `Step 2: Install` → `step-2-install`, a second `重複` → `重複-1`.
485
+ - A heading whose text keeps no character (`★`) has no id, its `ParseResult.headings[].id` is `""`, and its table-of-contents entry is plain text, not a link.
486
+ - The text is the heading's text as GitHub reads it: no Typographer substitution (`A -- B` → `a----b`), white space as written, an image's alt and raw HTML left out. The spaces and tabs before an ATX closing sequence or a `{…}` block are not text (`# Title ##` → `title`).
487
+ - Where the converter parses a construct differently from GitHub, the id follows the converter's rendering: `$ x $` (text here, a formula on GitHub), the number of a footnote referenced from inside another note, a code span continued on an indented line, `<<x>>`, Zenn directives and definition lists.
488
+ - **Anchors in raw HTML**: the viewer keeps `<a name="…">` and ids starting with `user-content-`. `<a id="user-content-faq"></a>` is reached by `#user-content-faq`. It removes any other author id (see [Raw HTML](#raw-html)).
489
+
490
+ Links written for GitHub or VS Code (`[概要へ](#概要)`, `[使い方](#api-の使い方)`) land on the heading.
491
+
330
492
  ### LaTeX Math Expressions
331
493
 
332
494
  - Inline math: `$y=x^2$`. A single `$` follows the `tex_math_dollars` rule of Pandoc 3.12: the opening `$` must be followed by a character that is not white space, and the formula ends at the first `$` after it that is not escaped by `\` and not inside a `\text{…}` group whose braces close; that `$` must follow a character that is not white space (a character escaped by `\`, such as the space in `$a\ $`, is not) and must not be followed by an ASCII digit; otherwise the `$` stays text.
@@ -1,6 +1,6 @@
1
- import { g as p, r as u, d as a } from "./chunk-MOJQB5TN-D_h6bolU.js";
2
- import { p as f } from "./chunk-JWPE2WC7-DpcSSOP5.js";
3
- import { _ as n, $ as o } from "./extractHeadings-CGJexznE.js";
1
+ import { g as p, r as u, d as a } from "./chunk-MOJQB5TN-1pZNp2Wl.js";
2
+ import { p as f } from "./chunk-JWPE2WC7-Djg6Qi23.js";
3
+ import { _ as n, O as o } from "./extractHeadings-CtiYkq2z.js";
4
4
  import { M as c, b as d } from "./cynefin-VYW2F7L2-Bkk-F_Js.js";
5
5
  var v = d().RailroadAbnf.parser.LangiumParser, i = /* @__PURE__ */ n((e) => {
6
6
  const r = e.alternatives.map(g);
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./chunk-MOJQB5TN-Cw864mUL.cjs"),u=require("./chunk-JWPE2WC7-5cM5nFf6.cjs"),n=require("./extractHeadings-lDHB2sBb.cjs"),i=require("./cynefin-VYW2F7L2-MpAliYy9.cjs");var p=i.createRailroadAbnfServices().RailroadAbnf.parser.LangiumParser,o=n.__name(e=>{const r=e.alternatives.map(c);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),c=n.__name(e=>{const r=e.elements.map(d);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),f=n.__name(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),d=n.__name(e=>{const r=b(e.primary);if(!e.repeat)return r;const{min:t,max:s}=f(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),b=n.__name(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return o(e.element);case"AbnfOptionalGroup":return{type:"optional",element:o(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),g=n.__name(e=>({name:e.name,definition:o(e.definition)}),"transformRule"),v=n.__name(e=>{u.populateCommonDb(e,a.db),e.title&&a.db.setTitle(e.title),e.rules.map(r=>a.db.addRule(g(r)))},"populateDb"),y={parse:n.__name(e=>{a.db.clear(),n.log.debug("[ABNF Parser] Starting Langium parse");const r=p.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new i.MermaidParseError(r);const t=r.value;n.log.debug("[ABNF Parser] Parsed rules:",t.rules.length),v(t),n.log.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a.db}},_={parser:y,db:a.db,renderer:a.renderer,styles:a.getStyles};exports.diagram=_;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./chunk-MOJQB5TN-DUk2Jy4_.cjs"),u=require("./chunk-JWPE2WC7-Bz9dXsLZ.cjs"),n=require("./extractHeadings-D1A8WzVy.cjs"),i=require("./cynefin-VYW2F7L2-MpAliYy9.cjs");var p=i.createRailroadAbnfServices().RailroadAbnf.parser.LangiumParser,o=n.__name(e=>{const r=e.alternatives.map(c);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),c=n.__name(e=>{const r=e.elements.map(d);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),f=n.__name(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),d=n.__name(e=>{const r=b(e.primary);if(!e.repeat)return r;const{min:t,max:s}=f(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),b=n.__name(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return o(e.element);case"AbnfOptionalGroup":return{type:"optional",element:o(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),g=n.__name(e=>({name:e.name,definition:o(e.definition)}),"transformRule"),v=n.__name(e=>{u.populateCommonDb(e,a.db),e.title&&a.db.setTitle(e.title),e.rules.map(r=>a.db.addRule(g(r)))},"populateDb"),y={parse:n.__name(e=>{a.db.clear(),n.log.debug("[ABNF Parser] Starting Langium parse");const r=p.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new i.MermaidParseError(r);const t=r.value;n.log.debug("[ABNF Parser] Parsed rules:",t.rules.length),v(t),n.log.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a.db}},_={parser:y,db:a.db,renderer:a.renderer,styles:a.getStyles};exports.diagram=_;
@@ -1 +1 @@
1
- "use strict";const n=require("./extractHeadings-lDHB2sBb.cjs");function $(o){return o.innerRadius}function b(o){return o.outerRadius}function k(o){return o.startAngle}function _(o){return o.endAngle}function nn(o){return o&&o.padAngle}function en(o,x,D,P,h,v,S,s){var E=D-o,r=P-x,e=S-h,p=s-v,i=p*E-e*r;if(!(i*i<n.epsilon))return i=(e*(x-v)-p*(o-h))/i,[o+i*E,x+i*r]}function M(o,x,D,P,h,v,S){var s=o-D,E=x-P,r=(S?v:-v)/n.sqrt(s*s+E*E),e=r*E,p=-r*s,i=o+e,u=x+p,c=D+e,f=P+p,j=(i+c)/2,l=(u+f)/2,y=c-i,g=f-u,A=y*y+g*g,R=h-v,T=i*f-c*u,H=(g<0?-1:1)*n.sqrt(n.max(0,R*R*A-T*T)),I=(T*g-y*H)/A,O=(-T*y-g*H)/A,q=(T*g+y*H)/A,d=(-T*y+g*H)/A,m=I-j,a=O-l,t=q-j,z=d-l;return m*m+a*a>t*t+z*z&&(I=q,O=d),{cx:I,cy:O,x01:-e,y01:-p,x11:I*(h/R-1),y11:O*(h/R-1)}}function an(){var o=$,x=b,D=n.constant(0),P=null,h=k,v=_,S=nn,s=null,E=n.withPath(r);function r(){var e,p,i=+o.apply(this,arguments),u=+x.apply(this,arguments),c=h.apply(this,arguments)-n.halfPi,f=v.apply(this,arguments)-n.halfPi,j=n.abs(f-c),l=f>c;if(s||(s=e=E()),u<i&&(p=u,u=i,i=p),!(u>n.epsilon))s.moveTo(0,0);else if(j>n.tau-n.epsilon)s.moveTo(u*n.cos(c),u*n.sin(c)),s.arc(0,0,u,c,f,!l),i>n.epsilon&&(s.moveTo(i*n.cos(f),i*n.sin(f)),s.arc(0,0,i,f,c,l));else{var y=c,g=f,A=c,R=f,T=j,H=j,I=S.apply(this,arguments)/2,O=I>n.epsilon&&(P?+P.apply(this,arguments):n.sqrt(i*i+u*u)),q=n.min(n.abs(u-i)/2,+D.apply(this,arguments)),d=q,m=q,a,t;if(O>n.epsilon){var z=n.asin(O/i*n.sin(I)),F=n.asin(O/u*n.sin(I));(T-=z*2)>n.epsilon?(z*=l?1:-1,A+=z,R-=z):(T=0,A=R=(c+f)/2),(H-=F*2)>n.epsilon?(F*=l?1:-1,y+=F,g-=F):(H=0,y=g=(c+f)/2)}var B=u*n.cos(y),C=u*n.sin(y),G=i*n.cos(R),J=i*n.sin(R);if(q>n.epsilon){var K=u*n.cos(g),L=u*n.sin(g),N=i*n.cos(A),Q=i*n.sin(A),w;if(j<n.pi)if(w=en(B,C,N,Q,K,L,G,J)){var U=B-w[0],V=C-w[1],W=K-w[0],X=L-w[1],Y=1/n.sin(n.acos((U*W+V*X)/(n.sqrt(U*U+V*V)*n.sqrt(W*W+X*X)))/2),Z=n.sqrt(w[0]*w[0]+w[1]*w[1]);d=n.min(q,(i-Z)/(Y-1)),m=n.min(q,(u-Z)/(Y+1))}else d=m=0}H>n.epsilon?m>n.epsilon?(a=M(N,Q,B,C,u,m,l),t=M(K,L,G,J,u,m,l),s.moveTo(a.cx+a.x01,a.cy+a.y01),m<q?s.arc(a.cx,a.cy,m,n.atan2(a.y01,a.x01),n.atan2(t.y01,t.x01),!l):(s.arc(a.cx,a.cy,m,n.atan2(a.y01,a.x01),n.atan2(a.y11,a.x11),!l),s.arc(0,0,u,n.atan2(a.cy+a.y11,a.cx+a.x11),n.atan2(t.cy+t.y11,t.cx+t.x11),!l),s.arc(t.cx,t.cy,m,n.atan2(t.y11,t.x11),n.atan2(t.y01,t.x01),!l))):(s.moveTo(B,C),s.arc(0,0,u,y,g,!l)):s.moveTo(B,C),!(i>n.epsilon)||!(T>n.epsilon)?s.lineTo(G,J):d>n.epsilon?(a=M(G,J,K,L,i,-d,l),t=M(B,C,N,Q,i,-d,l),s.lineTo(a.cx+a.x01,a.cy+a.y01),d<q?s.arc(a.cx,a.cy,d,n.atan2(a.y01,a.x01),n.atan2(t.y01,t.x01),!l):(s.arc(a.cx,a.cy,d,n.atan2(a.y01,a.x01),n.atan2(a.y11,a.x11),!l),s.arc(0,0,i,n.atan2(a.cy+a.y11,a.cx+a.x11),n.atan2(t.cy+t.y11,t.cx+t.x11),l),s.arc(t.cx,t.cy,d,n.atan2(t.y11,t.x11),n.atan2(t.y01,t.x01),!l))):s.arc(0,0,i,R,A,l)}if(s.closePath(),e)return s=null,e+""||null}return r.centroid=function(){var e=(+o.apply(this,arguments)+ +x.apply(this,arguments))/2,p=(+h.apply(this,arguments)+ +v.apply(this,arguments))/2-n.pi/2;return[n.cos(p)*e,n.sin(p)*e]},r.innerRadius=function(e){return arguments.length?(o=typeof e=="function"?e:n.constant(+e),r):o},r.outerRadius=function(e){return arguments.length?(x=typeof e=="function"?e:n.constant(+e),r):x},r.cornerRadius=function(e){return arguments.length?(D=typeof e=="function"?e:n.constant(+e),r):D},r.padRadius=function(e){return arguments.length?(P=e==null?null:typeof e=="function"?e:n.constant(+e),r):P},r.startAngle=function(e){return arguments.length?(h=typeof e=="function"?e:n.constant(+e),r):h},r.endAngle=function(e){return arguments.length?(v=typeof e=="function"?e:n.constant(+e),r):v},r.padAngle=function(e){return arguments.length?(S=typeof e=="function"?e:n.constant(+e),r):S},r.context=function(e){return arguments.length?(s=e??null,r):s},r}exports.d3arc=an;
1
+ "use strict";const n=require("./extractHeadings-D1A8WzVy.cjs");function $(o){return o.innerRadius}function b(o){return o.outerRadius}function k(o){return o.startAngle}function _(o){return o.endAngle}function nn(o){return o&&o.padAngle}function en(o,x,D,P,h,v,S,s){var E=D-o,r=P-x,e=S-h,p=s-v,i=p*E-e*r;if(!(i*i<n.epsilon))return i=(e*(x-v)-p*(o-h))/i,[o+i*E,x+i*r]}function M(o,x,D,P,h,v,S){var s=o-D,E=x-P,r=(S?v:-v)/n.sqrt(s*s+E*E),e=r*E,p=-r*s,i=o+e,u=x+p,c=D+e,f=P+p,j=(i+c)/2,l=(u+f)/2,y=c-i,g=f-u,A=y*y+g*g,R=h-v,T=i*f-c*u,H=(g<0?-1:1)*n.sqrt(n.max(0,R*R*A-T*T)),I=(T*g-y*H)/A,O=(-T*y-g*H)/A,q=(T*g+y*H)/A,d=(-T*y+g*H)/A,m=I-j,a=O-l,t=q-j,z=d-l;return m*m+a*a>t*t+z*z&&(I=q,O=d),{cx:I,cy:O,x01:-e,y01:-p,x11:I*(h/R-1),y11:O*(h/R-1)}}function an(){var o=$,x=b,D=n.constant(0),P=null,h=k,v=_,S=nn,s=null,E=n.withPath(r);function r(){var e,p,i=+o.apply(this,arguments),u=+x.apply(this,arguments),c=h.apply(this,arguments)-n.halfPi,f=v.apply(this,arguments)-n.halfPi,j=n.abs(f-c),l=f>c;if(s||(s=e=E()),u<i&&(p=u,u=i,i=p),!(u>n.epsilon))s.moveTo(0,0);else if(j>n.tau-n.epsilon)s.moveTo(u*n.cos(c),u*n.sin(c)),s.arc(0,0,u,c,f,!l),i>n.epsilon&&(s.moveTo(i*n.cos(f),i*n.sin(f)),s.arc(0,0,i,f,c,l));else{var y=c,g=f,A=c,R=f,T=j,H=j,I=S.apply(this,arguments)/2,O=I>n.epsilon&&(P?+P.apply(this,arguments):n.sqrt(i*i+u*u)),q=n.min(n.abs(u-i)/2,+D.apply(this,arguments)),d=q,m=q,a,t;if(O>n.epsilon){var z=n.asin(O/i*n.sin(I)),F=n.asin(O/u*n.sin(I));(T-=z*2)>n.epsilon?(z*=l?1:-1,A+=z,R-=z):(T=0,A=R=(c+f)/2),(H-=F*2)>n.epsilon?(F*=l?1:-1,y+=F,g-=F):(H=0,y=g=(c+f)/2)}var B=u*n.cos(y),C=u*n.sin(y),G=i*n.cos(R),J=i*n.sin(R);if(q>n.epsilon){var K=u*n.cos(g),L=u*n.sin(g),N=i*n.cos(A),Q=i*n.sin(A),w;if(j<n.pi)if(w=en(B,C,N,Q,K,L,G,J)){var U=B-w[0],V=C-w[1],W=K-w[0],X=L-w[1],Y=1/n.sin(n.acos((U*W+V*X)/(n.sqrt(U*U+V*V)*n.sqrt(W*W+X*X)))/2),Z=n.sqrt(w[0]*w[0]+w[1]*w[1]);d=n.min(q,(i-Z)/(Y-1)),m=n.min(q,(u-Z)/(Y+1))}else d=m=0}H>n.epsilon?m>n.epsilon?(a=M(N,Q,B,C,u,m,l),t=M(K,L,G,J,u,m,l),s.moveTo(a.cx+a.x01,a.cy+a.y01),m<q?s.arc(a.cx,a.cy,m,n.atan2(a.y01,a.x01),n.atan2(t.y01,t.x01),!l):(s.arc(a.cx,a.cy,m,n.atan2(a.y01,a.x01),n.atan2(a.y11,a.x11),!l),s.arc(0,0,u,n.atan2(a.cy+a.y11,a.cx+a.x11),n.atan2(t.cy+t.y11,t.cx+t.x11),!l),s.arc(t.cx,t.cy,m,n.atan2(t.y11,t.x11),n.atan2(t.y01,t.x01),!l))):(s.moveTo(B,C),s.arc(0,0,u,y,g,!l)):s.moveTo(B,C),!(i>n.epsilon)||!(T>n.epsilon)?s.lineTo(G,J):d>n.epsilon?(a=M(G,J,K,L,i,-d,l),t=M(B,C,N,Q,i,-d,l),s.lineTo(a.cx+a.x01,a.cy+a.y01),d<q?s.arc(a.cx,a.cy,d,n.atan2(a.y01,a.x01),n.atan2(t.y01,t.x01),!l):(s.arc(a.cx,a.cy,d,n.atan2(a.y01,a.x01),n.atan2(a.y11,a.x11),!l),s.arc(0,0,i,n.atan2(a.cy+a.y11,a.cx+a.x11),n.atan2(t.cy+t.y11,t.cx+t.x11),l),s.arc(t.cx,t.cy,d,n.atan2(t.y11,t.x11),n.atan2(t.y01,t.x01),!l))):s.arc(0,0,i,R,A,l)}if(s.closePath(),e)return s=null,e+""||null}return r.centroid=function(){var e=(+o.apply(this,arguments)+ +x.apply(this,arguments))/2,p=(+h.apply(this,arguments)+ +v.apply(this,arguments))/2-n.pi/2;return[n.cos(p)*e,n.sin(p)*e]},r.innerRadius=function(e){return arguments.length?(o=typeof e=="function"?e:n.constant(+e),r):o},r.outerRadius=function(e){return arguments.length?(x=typeof e=="function"?e:n.constant(+e),r):x},r.cornerRadius=function(e){return arguments.length?(D=typeof e=="function"?e:n.constant(+e),r):D},r.padRadius=function(e){return arguments.length?(P=e==null?null:typeof e=="function"?e:n.constant(+e),r):P},r.startAngle=function(e){return arguments.length?(h=typeof e=="function"?e:n.constant(+e),r):h},r.endAngle=function(e){return arguments.length?(v=typeof e=="function"?e:n.constant(+e),r):v},r.padAngle=function(e){return arguments.length?(S=typeof e=="function"?e:n.constant(+e),r):S},r.context=function(e){return arguments.length?(s=e??null,r):s},r}exports.d3arc=an;
@@ -0,0 +1,83 @@
1
+ import { aC as ln, aD as an, aE as y, aF as tn, aG as M, aH as D, aI as _, aJ as un, aK as rn, aL as S, aM as o, aN as H, aO as sn, aP as on, aQ as fn } from "./extractHeadings-CtiYkq2z.js";
2
+ function cn(l) {
3
+ return l.innerRadius;
4
+ }
5
+ function yn(l) {
6
+ return l.outerRadius;
7
+ }
8
+ function gn(l) {
9
+ return l.startAngle;
10
+ }
11
+ function dn(l) {
12
+ return l.endAngle;
13
+ }
14
+ function mn(l) {
15
+ return l && l.padAngle;
16
+ }
17
+ function pn(l, h, O, E, v, A, J, a) {
18
+ var q = O - l, i = E - h, n = J - v, d = a - A, u = d * q - n * i;
19
+ if (!(u * u < y))
20
+ return u = (n * (h - A) - d * (l - v)) / u, [l + u * q, h + u * i];
21
+ }
22
+ function W(l, h, O, E, v, A, J) {
23
+ var a = l - O, q = h - E, i = (J ? A : -A) / S(a * a + q * q), n = i * q, d = -i * a, u = l + n, s = h + d, f = O + n, c = E + d, K = (u + f) / 2, t = (s + c) / 2, m = f - u, g = c - s, R = m * m + g * g, T = v - A, P = u * c - f * s, C = (g < 0 ? -1 : 1) * S(fn(0, T * T * R - P * P)), F = (P * g - m * C) / R, G = (-P * m - g * C) / R, w = (P * g + m * C) / R, p = (-P * m + g * C) / R, x = F - K, e = G - t, r = w - K, L = p - t;
24
+ return x * x + e * e > r * r + L * L && (F = w, G = p), {
25
+ cx: F,
26
+ cy: G,
27
+ x01: -n,
28
+ y01: -d,
29
+ x11: F * (v / T - 1),
30
+ y11: G * (v / T - 1)
31
+ };
32
+ }
33
+ function hn() {
34
+ var l = cn, h = yn, O = H(0), E = null, v = gn, A = dn, J = mn, a = null, q = ln(i);
35
+ function i() {
36
+ var n, d, u = +l.apply(this, arguments), s = +h.apply(this, arguments), f = v.apply(this, arguments) - an, c = A.apply(this, arguments) - an, K = un(c - f), t = c > f;
37
+ if (a || (a = n = q()), s < u && (d = s, s = u, u = d), !(s > y)) a.moveTo(0, 0);
38
+ else if (K > tn - y)
39
+ a.moveTo(s * M(f), s * D(f)), a.arc(0, 0, s, f, c, !t), u > y && (a.moveTo(u * M(c), u * D(c)), a.arc(0, 0, u, c, f, t));
40
+ else {
41
+ var m = f, g = c, R = f, T = c, P = K, C = K, F = J.apply(this, arguments) / 2, G = F > y && (E ? +E.apply(this, arguments) : S(u * u + s * s)), w = _(un(s - u) / 2, +O.apply(this, arguments)), p = w, x = w, e, r;
42
+ if (G > y) {
43
+ var L = sn(G / u * D(F)), j = sn(G / s * D(F));
44
+ (P -= L * 2) > y ? (L *= t ? 1 : -1, R += L, T -= L) : (P = 0, R = T = (f + c) / 2), (C -= j * 2) > y ? (j *= t ? 1 : -1, m += j, g -= j) : (C = 0, m = g = (f + c) / 2);
45
+ }
46
+ var N = s * M(m), Q = s * D(m), z = u * M(T), B = u * D(T);
47
+ if (w > y) {
48
+ var U = s * M(g), V = s * D(g), X = u * M(R), Y = u * D(R), I;
49
+ if (K < rn)
50
+ if (I = pn(N, Q, X, Y, U, V, z, B)) {
51
+ var Z = N - I[0], $ = Q - I[1], k = U - I[0], b = V - I[1], nn = 1 / D(on((Z * k + $ * b) / (S(Z * Z + $ * $) * S(k * k + b * b))) / 2), en = S(I[0] * I[0] + I[1] * I[1]);
52
+ p = _(w, (u - en) / (nn - 1)), x = _(w, (s - en) / (nn + 1));
53
+ } else
54
+ p = x = 0;
55
+ }
56
+ C > y ? x > y ? (e = W(X, Y, N, Q, s, x, t), r = W(U, V, z, B, s, x, t), a.moveTo(e.cx + e.x01, e.cy + e.y01), x < w ? a.arc(e.cx, e.cy, x, o(e.y01, e.x01), o(r.y01, r.x01), !t) : (a.arc(e.cx, e.cy, x, o(e.y01, e.x01), o(e.y11, e.x11), !t), a.arc(0, 0, s, o(e.cy + e.y11, e.cx + e.x11), o(r.cy + r.y11, r.cx + r.x11), !t), a.arc(r.cx, r.cy, x, o(r.y11, r.x11), o(r.y01, r.x01), !t))) : (a.moveTo(N, Q), a.arc(0, 0, s, m, g, !t)) : a.moveTo(N, Q), !(u > y) || !(P > y) ? a.lineTo(z, B) : p > y ? (e = W(z, B, U, V, u, -p, t), r = W(N, Q, X, Y, u, -p, t), a.lineTo(e.cx + e.x01, e.cy + e.y01), p < w ? a.arc(e.cx, e.cy, p, o(e.y01, e.x01), o(r.y01, r.x01), !t) : (a.arc(e.cx, e.cy, p, o(e.y01, e.x01), o(e.y11, e.x11), !t), a.arc(0, 0, u, o(e.cy + e.y11, e.cx + e.x11), o(r.cy + r.y11, r.cx + r.x11), t), a.arc(r.cx, r.cy, p, o(r.y11, r.x11), o(r.y01, r.x01), !t))) : a.arc(0, 0, u, T, R, t);
57
+ }
58
+ if (a.closePath(), n) return a = null, n + "" || null;
59
+ }
60
+ return i.centroid = function() {
61
+ var n = (+l.apply(this, arguments) + +h.apply(this, arguments)) / 2, d = (+v.apply(this, arguments) + +A.apply(this, arguments)) / 2 - rn / 2;
62
+ return [M(d) * n, D(d) * n];
63
+ }, i.innerRadius = function(n) {
64
+ return arguments.length ? (l = typeof n == "function" ? n : H(+n), i) : l;
65
+ }, i.outerRadius = function(n) {
66
+ return arguments.length ? (h = typeof n == "function" ? n : H(+n), i) : h;
67
+ }, i.cornerRadius = function(n) {
68
+ return arguments.length ? (O = typeof n == "function" ? n : H(+n), i) : O;
69
+ }, i.padRadius = function(n) {
70
+ return arguments.length ? (E = n == null ? null : typeof n == "function" ? n : H(+n), i) : E;
71
+ }, i.startAngle = function(n) {
72
+ return arguments.length ? (v = typeof n == "function" ? n : H(+n), i) : v;
73
+ }, i.endAngle = function(n) {
74
+ return arguments.length ? (A = typeof n == "function" ? n : H(+n), i) : A;
75
+ }, i.padAngle = function(n) {
76
+ return arguments.length ? (J = typeof n == "function" ? n : H(+n), i) : J;
77
+ }, i.context = function(n) {
78
+ return arguments.length ? (a = n ?? null, i) : a;
79
+ }, i;
80
+ }
81
+ export {
82
+ hn as d
83
+ };