@mui/internal-docs-infra 0.11.1-canary.9 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/ChunkProvider/ChunkContext.d.mts +10 -0
  2. package/ChunkProvider/ChunkContext.mjs +15 -0
  3. package/ChunkProvider/ChunkProvider.d.mts +14 -0
  4. package/ChunkProvider/ChunkProvider.mjs +38 -0
  5. package/ChunkProvider/PreloadContext.d.mts +14 -0
  6. package/ChunkProvider/PreloadContext.mjs +18 -0
  7. package/ChunkProvider/PreloadProvider.d.mts +13 -0
  8. package/ChunkProvider/PreloadProvider.mjs +33 -0
  9. package/ChunkProvider/index.d.mts +7 -0
  10. package/ChunkProvider/index.mjs +7 -0
  11. package/ChunkProvider/types.d.mts +23 -0
  12. package/ChunkProvider/types.mjs +1 -0
  13. package/ChunkProvider/usePreload.d.mts +8 -0
  14. package/ChunkProvider/usePreload.mjs +21 -0
  15. package/CodeControllerContext/CodeControllerContext.d.mts +11 -0
  16. package/CodeControllerContext/CodeControllerContext.mjs +2 -1
  17. package/CodeHighlighter/CodeHighlighter.d.mts +15 -1
  18. package/CodeHighlighter/CodeHighlighter.mjs +97 -319
  19. package/CodeHighlighter/CodeHighlighterChunk.d.mts +42 -0
  20. package/CodeHighlighter/CodeHighlighterChunk.mjs +77 -0
  21. package/CodeHighlighter/CodeHighlighterClient.mjs +597 -128
  22. package/CodeHighlighter/CodeHighlighterContext.d.mts +57 -1
  23. package/CodeHighlighter/CodeHighlighterFallbackContext.d.mts +14 -2
  24. package/CodeHighlighter/CodeHighlighterFallbackContext.mjs +1 -3
  25. package/CodeHighlighter/CodeInitialSourceLoader.d.mts +10 -0
  26. package/CodeHighlighter/CodeInitialSourceLoader.mjs +108 -0
  27. package/CodeHighlighter/CodeSourceLoader.d.mts +11 -0
  28. package/CodeHighlighter/CodeSourceLoader.mjs +128 -0
  29. package/CodeHighlighter/buildCodeHighlighterChunkProps.d.mts +47 -0
  30. package/CodeHighlighter/buildCodeHighlighterChunkProps.mjs +61 -0
  31. package/CodeHighlighter/buildStringFallback.d.mts +29 -0
  32. package/CodeHighlighter/buildStringFallback.mjs +42 -0
  33. package/CodeHighlighter/codeToFallbackProps.d.mts +31 -2
  34. package/CodeHighlighter/codeToFallbackProps.mjs +347 -42
  35. package/CodeHighlighter/createClientProps.d.mts +17 -0
  36. package/CodeHighlighter/createClientProps.mjs +78 -0
  37. package/CodeHighlighter/errors.d.mts +6 -0
  38. package/CodeHighlighter/errors.mjs +10 -0
  39. package/CodeHighlighter/fallbackCompression.d.mts +96 -0
  40. package/CodeHighlighter/fallbackCompression.mjs +253 -0
  41. package/CodeHighlighter/fallbackFormat.d.mts +137 -0
  42. package/CodeHighlighter/fallbackFormat.mjs +422 -0
  43. package/CodeHighlighter/index.d.mts +4 -1
  44. package/CodeHighlighter/index.mjs +3 -1
  45. package/CodeHighlighter/mergeComments.d.mts +38 -0
  46. package/CodeHighlighter/mergeComments.mjs +80 -0
  47. package/CodeHighlighter/prepareInitialSource.d.mts +42 -0
  48. package/CodeHighlighter/prepareInitialSource.mjs +292 -0
  49. package/CodeHighlighter/resolveFallbackCritical.d.mts +23 -0
  50. package/CodeHighlighter/resolveFallbackCritical.mjs +44 -0
  51. package/CodeHighlighter/types.d.mts +272 -8
  52. package/CodeHighlighter/useCodeFallback.d.mts +94 -0
  53. package/CodeHighlighter/useCodeFallback.mjs +204 -0
  54. package/CodeHighlighter/useGrammarsReady.d.mts +18 -0
  55. package/CodeHighlighter/useGrammarsReady.mjs +45 -0
  56. package/CodeHighlighter/useSpeculativeCodePreload.d.mts +26 -0
  57. package/CodeHighlighter/useSpeculativeCodePreload.mjs +40 -0
  58. package/CodeHighlighter/useSpeculativeEditingPreload.d.mts +33 -0
  59. package/CodeHighlighter/useSpeculativeEditingPreload.mjs +58 -0
  60. package/CodeHighlighter/useSpeculativeGrammarPreload.d.mts +23 -0
  61. package/CodeHighlighter/useSpeculativeGrammarPreload.mjs +31 -0
  62. package/CodeHighlighter/useSpeculativeUseCodePreload.d.mts +22 -0
  63. package/CodeHighlighter/useSpeculativeUseCodePreload.mjs +41 -0
  64. package/CodeProvider/CodeContext.d.mts +47 -12
  65. package/CodeProvider/CodeContext.mjs +7 -0
  66. package/CodeProvider/CodeProvider.d.mts +4 -2
  67. package/CodeProvider/CodeProvider.mjs +40 -102
  68. package/CodeProvider/CodeProviderLazy.d.mts +40 -0
  69. package/CodeProvider/CodeProviderLazy.mjs +96 -0
  70. package/CodeProvider/constants.d.mts +26 -0
  71. package/CodeProvider/constants.mjs +24 -0
  72. package/CodeProvider/createParseSourceWorkerClient.d.mts +6 -0
  73. package/CodeProvider/createParseSourceWorkerClient.mjs +22 -2
  74. package/CodeProvider/index.d.mts +2 -1
  75. package/CodeProvider/index.mjs +9 -1
  76. package/CodeProvider/parseSourceWorker.mjs +33 -0
  77. package/CodeProvider/useCodeProviderValue.d.mts +54 -0
  78. package/CodeProvider/useCodeProviderValue.mjs +188 -0
  79. package/CoordinatedLazy/ChunkServerLoader.d.mts +25 -0
  80. package/CoordinatedLazy/ChunkServerLoader.mjs +97 -0
  81. package/CoordinatedLazy/CoordinatedContentContext.d.mts +15 -0
  82. package/CoordinatedLazy/CoordinatedContentContext.mjs +22 -0
  83. package/CoordinatedLazy/CoordinatedFallbackContext.d.mts +11 -0
  84. package/CoordinatedLazy/CoordinatedFallbackContext.mjs +13 -0
  85. package/CoordinatedLazy/CoordinatedGateContext.d.mts +14 -0
  86. package/CoordinatedLazy/CoordinatedGateContext.mjs +19 -0
  87. package/CoordinatedLazy/CoordinatedLazy.d.mts +14 -0
  88. package/CoordinatedLazy/CoordinatedLazy.mjs +86 -0
  89. package/CoordinatedLazy/CoordinatedLazyClient.d.mts +24 -0
  90. package/CoordinatedLazy/CoordinatedLazyClient.mjs +65 -0
  91. package/CoordinatedLazy/LazyContent.d.mts +26 -0
  92. package/CoordinatedLazy/LazyContent.mjs +80 -0
  93. package/CoordinatedLazy/LazyContentServer.d.mts +18 -0
  94. package/CoordinatedLazy/LazyContentServer.mjs +25 -0
  95. package/CoordinatedLazy/buildChunkRenderInputs.d.mts +8 -0
  96. package/CoordinatedLazy/buildChunkRenderInputs.mjs +35 -0
  97. package/CoordinatedLazy/createCoordinatedLazy.d.mts +32 -0
  98. package/CoordinatedLazy/createCoordinatedLazy.mjs +127 -0
  99. package/CoordinatedLazy/index.d.mts +14 -0
  100. package/CoordinatedLazy/index.mjs +18 -0
  101. package/CoordinatedLazy/resolveChunkRender.d.mts +26 -0
  102. package/CoordinatedLazy/resolveChunkRender.mjs +73 -0
  103. package/CoordinatedLazy/types.d.mts +408 -0
  104. package/CoordinatedLazy/types.mjs +1 -0
  105. package/CoordinatedLazy/useChunk.d.mts +30 -0
  106. package/CoordinatedLazy/useChunk.mjs +135 -0
  107. package/CoordinatedLazy/useCoordinatedFallback.d.mts +12 -0
  108. package/CoordinatedLazy/useCoordinatedFallback.mjs +40 -0
  109. package/CoordinatedLazy/useCoordinatedSwap.d.mts +16 -0
  110. package/CoordinatedLazy/useCoordinatedSwap.mjs +124 -0
  111. package/LICENSE +1 -1
  112. package/abstractCreateDemo/abstractCreateDemo.d.mts +54 -3
  113. package/abstractCreateDemo/abstractCreateDemo.mjs +47 -7
  114. package/abstractCreateDemo/resolveDemoFlag.d.mts +20 -0
  115. package/abstractCreateDemo/resolveDemoFlag.mjs +25 -0
  116. package/abstractCreateStream/abstractCreateStream.d.mts +18 -0
  117. package/abstractCreateStream/abstractCreateStream.mjs +45 -0
  118. package/abstractCreateStream/index.d.mts +2 -0
  119. package/abstractCreateStream/index.mjs +1 -0
  120. package/abstractCreateStream/types.d.mts +34 -0
  121. package/abstractCreateStream/types.mjs +1 -0
  122. package/abstractCreateTypes/TypeCode.mjs +12 -11
  123. package/abstractCreateTypes/typesToJsx.mjs +30 -9
  124. package/cli/ensureDemoClients.mjs +4 -148
  125. package/cli/ensureDemoPages.d.mts +45 -0
  126. package/cli/ensureDemoPages.mjs +99 -0
  127. package/cli/fileUtils/index.d.mts +11 -0
  128. package/cli/fileUtils/index.mjs +48 -0
  129. package/cli/findDemoIndexFiles.d.mts +15 -0
  130. package/cli/findDemoIndexFiles.mjs +121 -0
  131. package/cli/index.mjs +1 -1
  132. package/cli/loadNextConfig.d.mts +25 -0
  133. package/cli/loadNextConfig.mjs +60 -1
  134. package/cli/runBrowser.mjs +1 -1
  135. package/cli/runValidate.mjs +44 -1
  136. package/package.json +84 -4
  137. package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasis.mjs +30 -0
  138. package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.d.mts +17 -0
  139. package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.mjs +52 -0
  140. package/pipeline/hastUtils/frameFallbackFromSpans.d.mts +18 -0
  141. package/pipeline/hastUtils/frameFallbackFromSpans.mjs +24 -0
  142. package/pipeline/hastUtils/hast.d.mts +27 -0
  143. package/pipeline/hastUtils/hastCompression.d.mts +3 -1
  144. package/pipeline/hastUtils/hastCompression.mjs +9 -1
  145. package/pipeline/hastUtils/hastDecompress.mjs +10 -4
  146. package/pipeline/hastUtils/hastDictionary.mjs +9 -0
  147. package/pipeline/hastUtils/hastUtils.d.mts +4 -3
  148. package/pipeline/hastUtils/hastUtils.mjs +24 -12
  149. package/pipeline/hastUtils/index.d.mts +2 -1
  150. package/pipeline/hastUtils/index.mjs +2 -1
  151. package/pipeline/hastUtils/stripHighlightingSpans.d.mts +6 -2
  152. package/pipeline/hastUtils/stripHighlightingSpans.mjs +22 -10
  153. package/pipeline/lintJavascriptDemoFocus/lintJavascriptDemoFocus.mjs +10 -7
  154. package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.d.mts +31 -13
  155. package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.mjs +50 -55
  156. package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.d.mts +78 -0
  157. package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs +405 -0
  158. package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.d.mts +5 -5
  159. package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.mjs +36 -66
  160. package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.d.mts +23 -0
  161. package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs +92 -0
  162. package/pipeline/loadIsomorphicCodeVariant/decodeSource.d.mts +19 -0
  163. package/pipeline/loadIsomorphicCodeVariant/decodeSource.mjs +25 -0
  164. package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.d.mts +17 -0
  165. package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.mjs +26 -0
  166. package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +26 -2
  167. package/pipeline/loadIsomorphicCodeVariant/diffHast.mjs +563 -19
  168. package/pipeline/loadIsomorphicCodeVariant/embedTransforms.d.mts +49 -0
  169. package/pipeline/loadIsomorphicCodeVariant/embedTransforms.mjs +152 -0
  170. package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.d.mts +51 -0
  171. package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.mjs +161 -0
  172. package/pipeline/loadIsomorphicCodeVariant/flattenCodeVariant.mjs +6 -3
  173. package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.d.mts +12 -0
  174. package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.mjs +44 -0
  175. package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.d.mts +16 -0
  176. package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +74 -0
  177. package/pipeline/loadIsomorphicCodeVariant/loadCodeFallback.mjs +17 -5
  178. package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +229 -15
  179. package/pipeline/loadIsomorphicCodeVariant/transformSource.d.mts +2 -2
  180. package/pipeline/loadIsomorphicCodeVariant/transformSource.mjs +56 -22
  181. package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.d.mts +18 -0
  182. package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.mjs +11 -7
  183. package/pipeline/loadServerTypes/hastTypeUtils.d.mts +2 -2
  184. package/pipeline/loadServerTypes/hastTypeUtils.mjs +4 -4
  185. package/pipeline/loadServerTypes/loadServerTypes.mjs +1 -1
  186. package/pipeline/loadServerTypesMeta/extractJSDocText.d.mts +14 -0
  187. package/pipeline/loadServerTypesMeta/extractJSDocText.mjs +60 -0
  188. package/pipeline/loadServerTypesMeta/processTypes.mjs +43 -46
  189. package/pipeline/loadServerTypesText/order.mjs +1 -1
  190. package/pipeline/loadServerTypesText/parseTypesMarkdown.mjs +3 -1
  191. package/pipeline/loaderUtils/index.d.mts +0 -1
  192. package/pipeline/loaderUtils/index.mjs +0 -1
  193. package/pipeline/loaderUtils/parseImportsAndComments.d.mts +5 -1
  194. package/pipeline/loaderUtils/parseImportsAndComments.mjs +19 -9
  195. package/pipeline/loaderUtils/resolveModulePath.mjs +23 -1
  196. package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.d.mts +12 -0
  197. package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.mjs +17 -13
  198. package/pipeline/parseSource/addLineGutters.mjs +45 -11
  199. package/pipeline/parseSource/calculateFrameRanges.d.mts +22 -0
  200. package/pipeline/parseSource/calculateFrameRanges.mjs +69 -25
  201. package/pipeline/parseSource/detectGrammarScopes.d.mts +13 -0
  202. package/pipeline/parseSource/detectGrammarScopes.mjs +35 -0
  203. package/pipeline/parseSource/extendSyntaxTokens.mjs +501 -43
  204. package/pipeline/parseSource/frameVisibility.d.mts +47 -0
  205. package/pipeline/parseSource/frameVisibility.mjs +114 -0
  206. package/pipeline/parseSource/grammarCache.d.mts +33 -0
  207. package/pipeline/parseSource/grammarCache.mjs +73 -0
  208. package/pipeline/parseSource/grammarLoaders.d.mts +14 -0
  209. package/pipeline/parseSource/grammarLoaders.mjs +24 -0
  210. package/pipeline/parseSource/grammarMaps.d.mts +21 -1
  211. package/pipeline/parseSource/grammarMaps.mjs +36 -0
  212. package/pipeline/parseSource/isFrameSpan.d.mts +19 -0
  213. package/pipeline/parseSource/isFrameSpan.mjs +24 -0
  214. package/pipeline/parseSource/parseSource.d.mts +41 -6
  215. package/pipeline/parseSource/parseSource.mjs +184 -36
  216. package/pipeline/parseSource/redistributeFrameFallbacks.d.mts +40 -0
  217. package/pipeline/parseSource/redistributeFrameFallbacks.mjs +138 -0
  218. package/pipeline/parseSource/restructureFrames.d.mts +5 -0
  219. package/pipeline/parseSource/restructureFrames.mjs +179 -16
  220. package/pipeline/syncPageIndex/metadataToMarkdown.mjs +6 -2
  221. package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.d.mts +26 -0
  222. package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs +181 -114
  223. package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.d.mts +12 -0
  224. package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.mjs +52 -0
  225. package/pipeline/transformHtmlCodeInline/transformHtmlCodeInline.mjs +22 -1
  226. package/pipeline/transformTypescriptToJavascript/removeTypes.d.mts +5 -8
  227. package/pipeline/transformTypescriptToJavascript/removeTypes.mjs +27 -93
  228. package/useCode/EditableEngine.d.mts +233 -0
  229. package/useCode/EditableEngine.mjs +1712 -0
  230. package/useCode/EditingEngine.d.mts +13 -0
  231. package/useCode/EditingEngine.mjs +14 -0
  232. package/useCode/Pre.browser.mjs +5 -1
  233. package/useCode/Pre.d.mts +127 -1
  234. package/useCode/Pre.mjs +417 -165
  235. package/useCode/SourceEditingEngine.d.mts +50 -0
  236. package/useCode/SourceEditingEngine.mjs +461 -0
  237. package/useCode/TransformEngine.d.mts +39 -0
  238. package/useCode/TransformEngine.mjs +208 -0
  239. package/useCode/editingEngineCache.d.mts +29 -0
  240. package/useCode/editingEngineCache.mjs +68 -0
  241. package/useCode/sourceLineCounts.d.mts +80 -0
  242. package/useCode/sourceLineCounts.mjs +284 -0
  243. package/useCode/subscribeToggleNudge.d.mts +3 -0
  244. package/useCode/subscribeToggleNudge.mjs +95 -0
  245. package/useCode/transformEngineCache.d.mts +21 -0
  246. package/useCode/transformEngineCache.mjs +60 -0
  247. package/useCode/useCode.d.mts +140 -1
  248. package/useCode/useCode.mjs +250 -19
  249. package/useCode/useCodeUtils.d.mts +131 -20
  250. package/useCode/useCodeUtils.mjs +267 -194
  251. package/useCode/useCopyFunctionality.d.mts +13 -1
  252. package/useCode/useCopyFunctionality.mjs +39 -9
  253. package/useCode/useEditable.browser.mjs +10 -2
  254. package/useCode/useEditable.d.mts +27 -106
  255. package/useCode/useEditable.integration.browser.d.mts +1 -0
  256. package/useCode/useEditable.integration.browser.mjs +870 -0
  257. package/useCode/useEditable.mjs +198 -1247
  258. package/useCode/useEditableUtils.d.mts +50 -1
  259. package/useCode/useEditableUtils.mjs +29 -0
  260. package/useCode/useFileNavigation.d.mts +91 -3
  261. package/useCode/useFileNavigation.mjs +201 -41
  262. package/useCode/useHighlightGate.d.mts +17 -0
  263. package/useCode/useHighlightGate.mjs +147 -0
  264. package/useCode/useSourceEditing.d.mts +8 -0
  265. package/useCode/useSourceEditing.mjs +158 -314
  266. package/useCode/useSourceEnhancing.d.mts +5 -1
  267. package/useCode/useSourceEnhancing.mjs +22 -36
  268. package/useCode/useTransformManagement.d.mts +93 -5
  269. package/useCode/useTransformManagement.mjs +496 -28
  270. package/useCode/useTransitionPhase.d.mts +24 -0
  271. package/useCode/useTransitionPhase.mjs +49 -0
  272. package/useCode/useUIState.d.mts +2 -2
  273. package/useCode/useUIState.mjs +8 -8
  274. package/useCode/useVariantSelection.d.mts +130 -6
  275. package/useCode/useVariantSelection.mjs +529 -93
  276. package/useCodeWindow/useCodeWindow.d.mts +19 -2
  277. package/useCodeWindow/useCodeWindow.mjs +98 -71
  278. package/useCoordinated/coordinatePreference.d.mts +439 -0
  279. package/useCoordinated/coordinatePreference.mjs +951 -0
  280. package/useCoordinated/coordinatePreference.testUtils.d.mts +21 -0
  281. package/useCoordinated/coordinatePreference.testUtils.mjs +69 -0
  282. package/useCoordinated/createSettleGate.d.mts +96 -0
  283. package/useCoordinated/createSettleGate.mjs +171 -0
  284. package/useCoordinated/index.d.mts +8 -0
  285. package/useCoordinated/index.mjs +8 -0
  286. package/useCoordinated/layoutShiftGate.d.mts +24 -0
  287. package/useCoordinated/layoutShiftGate.mjs +79 -0
  288. package/useCoordinated/pageSettleGate.d.mts +11 -0
  289. package/useCoordinated/pageSettleGate.mjs +13 -0
  290. package/useCoordinated/scheduleTasks.d.mts +23 -0
  291. package/useCoordinated/scheduleTasks.mjs +45 -0
  292. package/useCoordinated/useCoordinated.d.mts +193 -0
  293. package/useCoordinated/useCoordinated.mjs +469 -0
  294. package/useCoordinated/useCoordinatedLazy.d.mts +17 -0
  295. package/useCoordinated/useCoordinatedLazy.mjs +38 -0
  296. package/useCoordinated/useCoordinatedLocalStorage.d.mts +16 -0
  297. package/useCoordinated/useCoordinatedLocalStorage.mjs +22 -0
  298. package/useCoordinated/useCoordinatedPreference.d.mts +20 -0
  299. package/useCoordinated/useCoordinatedPreference.mjs +26 -0
  300. package/useCoordinated/useSettleGate.d.mts +11 -0
  301. package/useCoordinated/useSettleGate.mjs +34 -0
  302. package/useDemo/exportVariant.d.mts +12 -5
  303. package/useDemo/exportVariant.mjs +59 -5
  304. package/useDemo/useDemo.d.mts +5 -2
  305. package/useScrollAnchor/useScrollAnchor.mjs +28 -5
  306. package/useStream/index.d.mts +6 -0
  307. package/useStream/index.mjs +6 -0
  308. package/useStream/streamChunks.d.mts +23 -0
  309. package/useStream/streamChunks.mjs +85 -0
  310. package/useStream/types.d.mts +45 -0
  311. package/useStream/types.mjs +1 -0
  312. package/useStream/useStream.d.mts +57 -0
  313. package/useStream/useStream.mjs +119 -0
  314. package/useStream/useStreamController.d.mts +15 -0
  315. package/useStream/useStreamController.mjs +90 -0
  316. package/withDocsInfra/withDocsInfra.d.mts +19 -0
  317. package/withDocsInfra/withDocsInfra.mjs +13 -5
  318. package/pipeline/loaderUtils/convertCommentsToOneIndexed.d.mts +0 -8
  319. package/pipeline/loaderUtils/convertCommentsToOneIndexed.mjs +0 -16
package/useCode/Pre.mjs CHANGED
@@ -2,134 +2,28 @@
2
2
 
3
3
  var _kbd;
4
4
  import * as React from 'react';
5
- import { toText } from 'hast-util-to-text';
6
5
  import { useEditable } from "./useEditable.mjs";
6
+ import { fallbackToHast, fallbackIsHighlighted } from "../CodeHighlighter/fallbackFormat.mjs";
7
7
  import { useCodeContext } from "../CodeProvider/CodeContext.mjs";
8
- import { hastToJsx, decompressHast } from "../pipeline/hastUtils/index.mjs";
8
+ import { hastToJsx, frameFallbackFromSpans } from "../pipeline/hastUtils/index.mjs";
9
+ import { stripHighlightingSpans } from "../pipeline/hastUtils/stripHighlightingSpans.mjs";
10
+ import { decodeHastSource } from "../pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs";
11
+ import { COLLAPSED_VISIBLE_FRAME_TYPES, resolveCollapsedFrameType, getInitialVisibleFrames } from "../pipeline/parseSource/frameVisibility.mjs";
12
+ import { isFrameSpan } from "../pipeline/parseSource/isFrameSpan.mjs";
13
+ import { getSourceLineCounts } from "./sourceLineCounts.mjs";
14
+ import { subscribeToggleNudge } from "./subscribeToggleNudge.mjs";
9
15
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
16
  const hastChildrenCache = new WeakMap();
11
- const textChildrenCache = new WeakMap();
17
+ const fallbackHastCache = new WeakMap();
12
18
 
13
- // Document-level subscriber registry for `<details>` toggle events. Each
14
- // `<Pre>` would otherwise install its own capture-phase listener; on docs
15
- // pages with many code blocks that's N listeners all firing on every
16
- // toggle anywhere in the document. A single shared listener fans out to
17
- // the relevant subscribers instead.
18
- //
19
- // Subscribers register their `<pre>` element so the dispatcher can do a
20
- // single `target.contains(pre)` ancestry check per subscriber and skip
21
- // the nudge entirely for unrelated toggles — no JS-side work runs in
22
- // `<Pre>` instances whose subtree the toggle didn't touch.
23
- //
24
- // The value is a Set rather than a single function so the registry
25
- // tolerates the (unlikely but possible) case where two `<Pre>` instances
26
- // transiently share the same DOM node — e.g. a fast unmount/remount
27
- // where the next mount's setup runs before the prior mount's cleanup.
28
- // Without the set the second subscribe would silently overwrite the
29
- // first nudge and a single unsubscribe would orphan the other instance.
30
-
31
- const toggleSubscribers = new Map();
32
- let toggleListenerAttached = false;
33
- let sharedToggleListener = null;
34
-
35
- // Reconcile the document-level capture listener with the current
36
- // subscriber set. Idempotent: callable from any code path (including
37
- // test teardowns that want to defensively assert no leaked listener)
38
- // without risk of leaving the document in a half-attached state.
39
- function syncToggleListener() {
40
- if (typeof document === 'undefined') {
41
- if (toggleSubscribers.size === 0) {
42
- sharedToggleListener = null;
43
- toggleListenerAttached = false;
44
- }
45
- return;
46
- }
47
- if (toggleSubscribers.size === 0) {
48
- if (toggleListenerAttached && sharedToggleListener) {
49
- document.removeEventListener('toggle', sharedToggleListener, true);
50
- }
51
- sharedToggleListener = null;
52
- toggleListenerAttached = false;
53
- return;
54
- }
55
- if (!toggleListenerAttached || !sharedToggleListener) {
56
- sharedToggleListener = event => {
57
- const target = event.target;
58
- if (!(target instanceof Node)) {
59
- return;
60
- }
61
- // Snapshot before iterating: a nudge may synchronously trigger an
62
- // unmount that mutates `toggleSubscribers` mid-dispatch. Iterating
63
- // a snapshot keeps dispatch order independent of subscriber
64
- // mutations and matches the snapshot pattern used by
65
- // `sweepDetachedFrames` / `nudgeFrameObserver`.
66
- Array.from(toggleSubscribers).forEach(([preNode, nudges]) => {
67
- // Centralized ancestry filter: only nudge subscribers whose `<pre>`
68
- // is a descendant of the toggled element. Done here (rather than
69
- // in each subscriber) so unrelated toggles short-circuit before
70
- // any subscriber-side work runs.
71
- if (!target.contains(preNode)) {
72
- return;
73
- }
74
- Array.from(nudges).forEach(nudge => nudge());
75
- });
76
- };
77
- document.addEventListener('toggle', sharedToggleListener, true);
78
- toggleListenerAttached = true;
79
- }
80
- }
81
- function subscribeToggleNudge(preNode, nudge) {
82
- // Defensive SSR no-op: there is no `document` to attach a listener to,
83
- // and module state in Node persists across requests — leaking a
84
- // subscriber here would also leak the closure it captures. `useEffect`
85
- // already won't run on the server, but make the contract explicit so
86
- // any future non-effect caller can't strand entries in the registry.
87
- if (typeof document === 'undefined') {
88
- return () => {};
89
- }
90
- let nudges = toggleSubscribers.get(preNode);
91
- if (!nudges) {
92
- nudges = new Set();
93
- toggleSubscribers.set(preNode, nudges);
94
- }
95
- nudges.add(nudge);
96
- syncToggleListener();
97
- return () => {
98
- const existing = toggleSubscribers.get(preNode);
99
- if (existing) {
100
- existing.delete(nudge);
101
- if (existing.size === 0) {
102
- toggleSubscribers.delete(preNode);
103
- }
104
- }
105
- syncToggleListener();
106
- };
107
- }
108
- const INITIAL_VISIBLE_FRAME_TYPES = new Set(['highlighted', 'focus', 'padding-top', 'padding-bottom']);
109
- function getInitialVisibleFrames(hast) {
110
- if (!hast) {
111
- return {
112
- 0: true
113
- };
114
- }
115
- const visibleFrames = {};
116
- let frameIndex = 0;
117
- let hasVisibleEmphasisFrame = false;
118
- hast.children.forEach(child => {
119
- if (child.type !== 'element' || child.properties.className !== 'frame') {
120
- return;
121
- }
122
- const frameType = child.properties.dataFrameType;
123
- if (typeof frameType === 'string' && INITIAL_VISIBLE_FRAME_TYPES.has(frameType)) {
124
- visibleFrames[frameIndex] = true;
125
- hasVisibleEmphasisFrame = true;
126
- }
127
- frameIndex += 1;
128
- });
129
- if (!hasVisibleEmphasisFrame && frameIndex > 0) {
130
- visibleFrames[0] = true;
131
- }
132
- return visibleFrames;
19
+ // Safety cap on `visibleFrames`-driven re-arms of the transition
20
+ // settle wait. The legitimate path consumes only a handful of
21
+ // re-arms per paused window; the cap is high enough that real IO
22
+ // settling never trips it but bounds any pathological loop.
23
+ const MAX_TRANSITION_REARMS = 32;
24
+ function resolveFrameTypeAttribute(frameType, collapseToEmpty) {
25
+ const resolved = resolveCollapsedFrameType(frameType, collapseToEmpty);
26
+ return resolved && resolved !== 'normal' ? resolved : undefined;
133
27
  }
134
28
 
135
29
  /**
@@ -217,7 +111,14 @@ function countFrameNewlinesOnly(node) {
217
111
  * `data-frame-indent` is encoded as `leadingSpaces / indentation`, so the
218
112
  * column count is `indent * indentation`.
219
113
  */
220
- function computeCollapsedBounds(hast, indentation) {
114
+ function computeCollapsedBounds(hast, indentation, collapseToEmpty = false) {
115
+ // Collapse-to-empty has no visible-when-collapsed region, so there are no bounds
116
+ // to constrain the caret to. (The original frame types are still `focus` /
117
+ // `highlighted` here — only their *rendered* type is rewritten — so this must
118
+ // be checked explicitly rather than inferred from the frames.)
119
+ if (collapseToEmpty) {
120
+ return undefined;
121
+ }
221
122
  if (!hast || hast.data?.collapsible !== true) {
222
123
  return undefined;
223
124
  }
@@ -226,12 +127,12 @@ function computeCollapsedBounds(hast, indentation) {
226
127
  let maxRow;
227
128
  let row = 0;
228
129
  for (const child of hast.children) {
229
- if (child.type !== 'element' || child.properties.className !== 'frame') {
130
+ if (child.type !== 'element' || !isFrameSpan(child)) {
230
131
  continue;
231
132
  }
232
133
  const frameType = child.properties.dataFrameType;
233
134
  const indent = child.properties.dataFrameIndent;
234
- const isVisibleWhenCollapsed = typeof frameType === 'string' && INITIAL_VISIBLE_FRAME_TYPES.has(frameType);
135
+ const isVisibleWhenCollapsed = typeof frameType === 'string' && COLLAPSED_VISIBLE_FRAME_TYPES.has(frameType);
235
136
  if (!isVisibleWhenCollapsed) {
236
137
  // Once we've passed the visible region, hidden frames can't change
237
138
  // any output — bail out entirely.
@@ -266,7 +167,7 @@ function computeCollapsedBounds(hast, indentation) {
266
167
  maxRow
267
168
  };
268
169
  }
269
- function renderCode(hastChildren, renderHast, text) {
170
+ function renderCode(hastChildren, renderHast, fallback) {
270
171
  if (renderHast) {
271
172
  let jsx = hastChildrenCache.get(hastChildren);
272
173
  if (!jsx) {
@@ -278,54 +179,167 @@ function renderCode(hastChildren, renderHast, text) {
278
179
  }
279
180
  return jsx;
280
181
  }
281
- if (text !== undefined) {
282
- return text;
182
+
183
+ // Server-rendered / pre-hydration fallback: drop highlighting spans but
184
+ // keep frame + collapse placeholders + link structure so the rendered
185
+ // block matches the height of the fully-highlighted version. This avoids
186
+ // a layout shift when a frame swaps from fallback to highlighted on
187
+ // intersection.
188
+ //
189
+ // Prefer a precomputed fallback (set on `frame.data.fallback` by
190
+ // `addLineGutters` for multi-frame splits) — usually a single text node —
191
+ // so the renderer skips the per-frame `stripHighlightingSpans` walk.
192
+ if (fallback) {
193
+ let jsx = fallbackHastCache.get(fallback);
194
+ if (!jsx) {
195
+ jsx = hastToJsx({
196
+ type: 'root',
197
+ children: fallback
198
+ });
199
+ fallbackHastCache.set(fallback, jsx);
200
+ }
201
+ return jsx;
283
202
  }
284
- let txt = textChildrenCache.get(hastChildren);
285
- if (!txt) {
286
- txt = toText({
203
+ let jsx = fallbackHastCache.get(hastChildren);
204
+ if (!jsx) {
205
+ jsx = hastToJsx({
287
206
  type: 'root',
288
- children: hastChildren
289
- }, {
290
- whitespace: 'pre'
207
+ children: frameFallbackFromSpans(hastChildren)
291
208
  });
292
- textChildrenCache.set(hastChildren, txt);
209
+ fallbackHastCache.set(hastChildren, jsx);
293
210
  }
294
- return txt;
211
+ return jsx;
212
+ }
213
+ function renderFallbackChild(child) {
214
+ // Same fallback path as `renderCode` but for top-level non-frame children
215
+ // (e.g. text whitespace between frames). Caching is keyed by the parent
216
+ // children array in `renderFrames` via React reconciliation; the
217
+ // structural cost here is bounded by hast size.
218
+ const stripped = stripHighlightingSpans({
219
+ type: 'root',
220
+ children: [child]
221
+ });
222
+ return hastToJsx(stripped);
295
223
  }
296
224
  export function Pre({
297
225
  children,
298
226
  className,
299
227
  fileName,
228
+ bridgeLineMode = 'focus',
300
229
  language,
301
230
  ref,
302
231
  setSource,
303
232
  shouldHighlight,
304
233
  hydrateMargin = '200px 0px 200px 0px',
234
+ fallback,
235
+ fallbackLineCounts,
305
236
  expanded = false,
306
- expand
237
+ collapseToEmpty = false,
238
+ expand,
239
+ transforming,
240
+ onTransitionReady,
241
+ swapTarget,
242
+ editActivation,
243
+ onActivate
307
244
  }) {
245
+ // Defer the decompressing `decodeHastSource` to a post-paint render ONLY when the
246
+ // first-paint `.fallback` is ALREADY highlighted — i.e. the promoted highlighted-visible
247
+ // fallback the server ships for `highlightAt: 'init'`. Then paint that highlighted
248
+ // fallback first (no decompression on the critical path) and swap in the full decoded
249
+ // tree after. When the fallback is plain — every other mode, including a late-mounted
250
+ // `'hydration'` block where `shouldHighlight` is also true on the first render — decode
251
+ // on mount instead, so we never flash plain → highlighted.
252
+ const [deferInitialDecode] = React.useState(() => shouldHighlight === true && !!fallback && fallbackIsHighlighted(fallback) && typeof children !== 'string');
253
+ const [decodeAllowed, setDecodeAllowed] = React.useState(!deferInitialDecode);
254
+ React.useEffect(() => {
255
+ if (deferInitialDecode) {
256
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional post-paint latch: flip after the first paint so the highlighted fallback shows before the decompressing decode runs
257
+ setDecodeAllowed(true);
258
+ }
259
+ }, [deferInitialDecode]);
260
+
261
+ // The variant `fallback` is forwarded to `decodeHastSource` so the
262
+ // `hastCompressed` payload is decompressed with the matching DEFLATE
263
+ // dictionary and each frame's `data.fallback` is restored. The decoded
264
+ // tree stays shared (read-only), since `Pre` only reads it.
308
265
  const hast = React.useMemo(() => {
309
- if (typeof children === 'string') {
266
+ if (!children || typeof children === 'string' || !decodeAllowed) {
310
267
  return null;
311
268
  }
312
- if ('hastJson' in children) {
313
- return JSON.parse(children.hastJson);
269
+ return decodeHastSource(children, fallback);
270
+ }, [children, fallback, decodeAllowed]);
271
+
272
+ // Variant-swap bridge descriptor. While a variant swap is in flight
273
+ // and the partner variant is taller than this one, we render an
274
+ // extra `<span class="collapse">` inside the appropriate frame so
275
+ // consumer CSS can animate the missing height before/after the swap
276
+ // commits. The bridge is JSX-only — `hast` itself is left pristine
277
+ // so caret bounds, line-gutter math, and the `visibleFrames` IO
278
+ // seeding all stay anchored to the real tree.
279
+ const bridge = React.useMemo(() => {
280
+ if (!hast || !transforming || !swapTarget) {
281
+ return null;
314
282
  }
315
- if ('hastCompressed' in children) {
316
- return JSON.parse(decompressHast(children.hastCompressed));
283
+ const {
284
+ totalLines: currentTotal,
285
+ focusedLines: rawCurrentFocused
286
+ } = getSourceLineCounts(hast);
287
+ // Collapse-to-empty collapses to an empty window, so the focused size is 0.
288
+ const currentFocused = collapseToEmpty ? 0 : rawCurrentFocused;
289
+ const compareFocused = bridgeLineMode === 'focus' && !expanded;
290
+ const current = compareFocused ? currentFocused : currentTotal;
291
+ const target = compareFocused ? swapTarget.focusedLines : swapTarget.totalLines;
292
+ const lines = target - current;
293
+ if (lines <= 0) {
294
+ return null;
295
+ }
296
+ // Pick the frame the bridge lands in:
297
+ // - collapsed: the last frame that's visible-by-default (so the
298
+ // placeholder sits inside the focus window).
299
+ // - expanded: the last frame overall (placeholder appears at
300
+ // the bottom of the fully-rendered block).
301
+ let frameIndex = -1;
302
+ let candidate = -1;
303
+ for (let i = 0; i < hast.children.length; i += 1) {
304
+ const child = hast.children[i];
305
+ if (child.type !== 'element' || !isFrameSpan(child)) {
306
+ continue;
307
+ }
308
+ frameIndex += 1;
309
+ if (!expanded) {
310
+ const frameType = resolveCollapsedFrameType(typeof child.properties.dataFrameType === 'string' ? child.properties.dataFrameType : undefined, collapseToEmpty);
311
+ if (frameType && COLLAPSED_VISIBLE_FRAME_TYPES.has(frameType)) {
312
+ candidate = frameIndex;
313
+ }
314
+ } else {
315
+ candidate = frameIndex;
316
+ }
317
+ }
318
+ if (candidate < 0) {
319
+ return null;
317
320
  }
318
- return children;
319
- }, [children]);
321
+ return {
322
+ frameIndex: candidate,
323
+ lines
324
+ };
325
+ }, [hast, transforming, swapTarget, expanded, bridgeLineMode, collapseToEmpty]);
320
326
  const preRef = React.useRef(null);
321
327
 
322
- // useEditable uses ref.current in its effect deps. On first render it's null
323
- // (set later by the callback ref), so the deps change on the next render,
324
- // causing contentEditable to flash and the cursor to be lost. Delaying
325
- // enablement by one synchronous re-render ensures the ref is already set
326
- // when useEditable first activates, keeping deps stable afterward.
328
+ // useEditable activates its engine in an effect gated on `disabled`, reading
329
+ // `preRef.current` at that point. On first render the ref is still null (the
330
+ // callback ref runs later), so we keep the block `disabled` for one
331
+ // synchronous re-render and flip `editableReady` true in a layout effect —
332
+ // by the time `disabled` goes false, `preRef.current` is populated and the
333
+ // engine attaches to a real node, avoiding a contentEditable flash / lost
334
+ // cursor on first paint.
327
335
  const [editableReady, setEditableReady] = React.useState(false);
328
336
  React.useLayoutEffect(() => {
337
+ // Deliberate two-pass mount gate: defer engine activation until the
338
+ // `bindPre` callback ref has committed (see 527-533). Flipping this true
339
+ // in a layout effect is the documented trigger for the no-flash /
340
+ // cursor-retention behavior and isn't derivable during render (the ref is
341
+ // intentionally null in render 1).
342
+ // eslint-disable-next-line react-hooks/set-state-in-effect
329
343
  setEditableReady(true);
330
344
  }, []);
331
345
  const onEditableChange = React.useCallback((text, position, preParsed) => {
@@ -338,7 +352,8 @@ export function Pre({
338
352
  // `setSource` (4th arg) where the host can stash it in a per-file cache
339
353
  // so the synchronous `parseControlledCode` pass can reuse it.
340
354
  const {
341
- parseSourceAsync
355
+ parseSourceAsync,
356
+ editingEngineLoader
342
357
  } = useCodeContext();
343
358
  const preParse = React.useMemo(() => {
344
359
  if (!setSource || !parseSourceAsync || !fileName) {
@@ -346,7 +361,50 @@ export function Pre({
346
361
  }
347
362
  return (text, _position, signal) => parseSourceAsync(text, fileName, language, signal);
348
363
  }, [setSource, parseSourceAsync, fileName, language]);
349
- const [visibleFrames, setVisibleFrames] = React.useState(() => getInitialVisibleFrames(hast));
364
+ const [visibleFrames, setVisibleFrames] = React.useState(() => getInitialVisibleFrames(hast, collapseToEmpty));
365
+
366
+ // Re-seed `visibleFrames` whenever the parsed tree identity changes
367
+ // (e.g. a transform swap such as JS↔TS, where the host keeps `<Pre>`
368
+ // mounted — see `getPreRenderKey` in `useFileNavigation`). Without
369
+ // this, frame indices computed from a prior tree leak into the new
370
+ // one: any emphasis frames that should be visible on first render of
371
+ // the new tree would stay un-hydrated until the IntersectionObserver
372
+ // corrects them (or indefinitely in environments without IO).
373
+ //
374
+ // We *union* the new initial-visible set onto whatever is currently
375
+ // visible rather than replacing outright. Replacing would drop frames
376
+ // hydrated by IO/editing in the prior tree before IO has a chance to
377
+ // re-run, causing a visible flash. Stale indices that no longer map
378
+ // to a frame in the new tree are harmless — the render loop skips
379
+ // them, and IO prunes them on the next pass.
380
+ //
381
+ // Runs in `useLayoutEffect` so the merged state commits before paint,
382
+ // keeping the update outside the render phase while still avoiding a
383
+ // visible flash of un-hydrated emphasis frames.
384
+ React.useLayoutEffect(() => {
385
+ // Next state unions the new initial-visible set onto `prev` rather than
386
+ // replacing it (see 564-597): replacing would drop frames already
387
+ // hydrated by IO/editing on the prior tree, causing the visible flash this
388
+ // guards against. Depends on both a prop (`hast`) and prior state, so it
389
+ // can't be derived during render.
390
+ // eslint-disable-next-line react-hooks/set-state-in-effect
391
+ setVisibleFrames(prev => {
392
+ const initial = getInitialVisibleFrames(hast, collapseToEmpty);
393
+ let merged;
394
+ Object.keys(initial).forEach(key => {
395
+ const index = Number(key);
396
+ if (prev[index] !== true) {
397
+ if (!merged) {
398
+ merged = {
399
+ ...prev
400
+ };
401
+ }
402
+ merged[index] = true;
403
+ }
404
+ });
405
+ return merged || prev;
406
+ });
407
+ }, [hast, collapseToEmpty]);
350
408
 
351
409
  // When the code block is collapsible AND currently collapsed, derive the
352
410
  // visible region's row range and minimum indent column so that:
@@ -358,7 +416,7 @@ export function Pre({
358
416
  // `data-frame-indent` is encoded as `leadingSpaces / 2`, matching the
359
417
  // hardcoded `indentation: 2` below.
360
418
  const indentation = 2;
361
- const collapsedBounds = React.useMemo(() => expanded ? undefined : computeCollapsedBounds(hast, indentation), [hast, expanded]);
419
+ const collapsedBounds = React.useMemo(() => expanded ? undefined : computeCollapsedBounds(hast, indentation, collapseToEmpty), [hast, expanded, collapseToEmpty]);
362
420
  useEditable(preRef, onEditableChange, {
363
421
  indentation,
364
422
  disabled: !setSource || !editableReady,
@@ -374,12 +432,109 @@ export function Pre({
374
432
  // every selectable row. Only set when the highlighter has actually
375
433
  // produced `.line` elements.
376
434
  caretSelector: shouldHighlight ? '.line' : undefined,
377
- preParse
435
+ preParse,
436
+ engineLoader: editingEngineLoader,
437
+ activation: editActivation,
438
+ onActivate
378
439
  });
379
440
  const observer = React.useRef(null);
380
441
  const observedFrames = React.useRef(new Set());
381
442
  const frameIndexMap = React.useRef(new WeakMap());
382
443
 
444
+ // Mirror `transforming` in a ref so the IO callback can read the latest
445
+ // value without re-creating itself (which would re-run the setup effect
446
+ // and tear down the observer mid-animation). The IO callback only
447
+ // suppresses for the active values (`'expanding'` / `'collapsing'`)
448
+ // — the paused values (`'collapsed'` / `'expanded'`) let IO run so
449
+ // the visible-frame set can reconcile before the host kicks off the
450
+ // keyframe animation. While the animation is running, newly revealed
451
+ // (or newly clipped) frames must not upgrade plain-text spans to
452
+ // highlighted HAST mid-animation — that DOM rebuild is visible to the
453
+ // user as a jump even though the bounding rect doesn't change.
454
+ const transformingRef = React.useRef(transforming ?? null);
455
+ React.useLayoutEffect(() => {
456
+ transformingRef.current = transforming ?? null;
457
+ }, [transforming]);
458
+
459
+ // Notify the host once the paused phase is fully reconciled so it
460
+ // can flip to the matching active value and start the CSS
461
+ // animation. "Fully reconciled" means three things:
462
+ //
463
+ // 1. If `shouldHighlight`, the highlighted `hast` has arrived
464
+ // (otherwise the animation would run against fallback text
465
+ // spans that are about to be replaced).
466
+ // 2. The IntersectionObserver has had a chance to fire and the
467
+ // resulting `visibleFrames` updates have committed, swapping
468
+ // every visible frame from plain text to highlighted HAST.
469
+ // We detect "settled" by re-arming on every `visibleFrames`
470
+ // change: each update cancels the pending callback and starts
471
+ // a fresh wait, so the callback only fires after the
472
+ // visibility set stops changing.
473
+ // 3. One animation frame has elapsed, giving the swapped-in
474
+ // HAST + `.collapse` bridge a paint cycle before the keyframes
475
+ // run.
476
+ //
477
+ // Without (1) the animation can fire against raw-text spans that
478
+ // haven't been upgraded. Without (2) a frame that's about to swap
479
+ // text→hast can do so mid-animation, producing a structural jump.
480
+ // Without (3) the bridge geometry may not yet reflect the
481
+ // committed tree.
482
+ //
483
+ // When `shouldHighlight` is false there is no `.collapse` bridge to
484
+ // animate (see the `bridge` memo, which bails on `!hast`), so we
485
+ // skip the hast/visibility waits and release on the next frame
486
+ // instead of deadlocking the swap. The setTimeout(0) step lets any
487
+ // already-queued IO callbacks flush before we sample
488
+ // `visibleFrames` for the final time; the rAF after it covers
489
+ // paint. `onTransitionReady` is stored in a ref so callback
490
+ // identity changes don't restart the wait.
491
+ const onTransitionReadyRef = React.useRef(onTransitionReady);
492
+ React.useLayoutEffect(() => {
493
+ onTransitionReadyRef.current = onTransitionReady;
494
+ }, [onTransitionReady]);
495
+ // Defense-in-depth cap on how many times the "settled" wait can be
496
+ // re-armed inside a single paused window. The legitimate path
497
+ // re-arms at most a handful of times (initial mount + the IO
498
+ // callbacks for the visible-frame set). If something pathological
499
+ // keeps `visibleFrames` churning faster than a macrotask, the cap
500
+ // ensures we still notify the host instead of livelocking the
501
+ // animation handshake.
502
+ const transitionRearmsRef = React.useRef(0);
503
+ const transitionLastPhaseRef = React.useRef(null);
504
+ React.useLayoutEffect(() => {
505
+ if (transforming !== transitionLastPhaseRef.current) {
506
+ transitionRearmsRef.current = 0;
507
+ transitionLastPhaseRef.current = transforming;
508
+ }
509
+ if (transforming !== 'collapsed' && transforming !== 'expanded') {
510
+ return undefined;
511
+ }
512
+ if (shouldHighlight && !hast) {
513
+ return undefined;
514
+ }
515
+ if (typeof requestAnimationFrame !== 'function') {
516
+ onTransitionReadyRef.current?.();
517
+ return undefined;
518
+ }
519
+ transitionRearmsRef.current += 1;
520
+ if (transitionRearmsRef.current > MAX_TRANSITION_REARMS) {
521
+ onTransitionReadyRef.current?.();
522
+ return undefined;
523
+ }
524
+ let rafId = null;
525
+ const taskId = setTimeout(() => {
526
+ rafId = requestAnimationFrame(() => {
527
+ onTransitionReadyRef.current?.();
528
+ });
529
+ }, 0);
530
+ return () => {
531
+ clearTimeout(taskId);
532
+ if (rafId !== null) {
533
+ cancelAnimationFrame(rafId);
534
+ }
535
+ };
536
+ }, [transforming, hast, shouldHighlight, visibleFrames]);
537
+
383
538
  // Drop frame spans that have been detached from the DOM. Used as a
384
539
  // defensive sweep in `nudgeFrameObserver` (and the IO effect) so the
385
540
  // tracking sets don't grow unboundedly across re-renders, even on
@@ -493,6 +648,24 @@ export function Pre({
493
648
  setPreNode(root);
494
649
  }, []);
495
650
  const handleIntersection = React.useCallback(entries => {
651
+ // Suppress visibility flips while a collapse/expand keyframe
652
+ // animation is actively running. The animation resizes ancestors
653
+ // and can momentarily clip or unclip frames; allowing the IO to
654
+ // act on those transient states would rebuild the rendered HAST
655
+ // (plain text → highlighted spans, or vice versa) in the middle
656
+ // of the animation, producing a visible structural jump.
657
+ //
658
+ // The paused phases (`'collapsed'` / `'expanded'`) intentionally
659
+ // do *not* suppress: those windows exist so the host can wait for
660
+ // the visible-frame set to reconcile (and any plain-text frames
661
+ // to upgrade to highlighted HAST) before kicking the animation
662
+ // off. The effect below calls `nudgeFrameObserver` once
663
+ // `transforming` settles back to `null`, which re-fires the
664
+ // observer against the post-animation layout so any genuine
665
+ // visibility changes are picked up then.
666
+ if (transformingRef.current === 'expanding' || transformingRef.current === 'collapsing') {
667
+ return;
668
+ }
496
669
  setVisibleFrames(prev => {
497
670
  const visible = [];
498
671
  const invisible = [];
@@ -591,6 +764,19 @@ export function Pre({
591
764
  unsubscribeToggle();
592
765
  };
593
766
  }, [preNode, hydrateMargin, handleIntersection, nudgeFrameObserver, sweepDetachedFrames]);
767
+
768
+ // Once a transform-swap animation settles back to `null`, re-evaluate
769
+ // every tracked frame so any genuine visibility changes that occurred
770
+ // during the animation (e.g. a frame that scrolled into view because
771
+ // the swap reflowed the page, or a frame whose collapsed-state height
772
+ // changed) are picked up now — not silently dropped along with the
773
+ // intermediate states `handleIntersection` ignored above.
774
+ React.useEffect(() => {
775
+ if (transforming) {
776
+ return;
777
+ }
778
+ nudgeFrameObserver();
779
+ }, [transforming, nudgeFrameObserver]);
594
780
  const observeFrame = React.useCallback(node => {
595
781
  if (!node) {
596
782
  // React 17/18 invoke ref callbacks with `null` on detach but
@@ -639,29 +825,55 @@ export function Pre({
639
825
  }
640
826
  return null;
641
827
  }
642
- if (child.properties.className === 'frame') {
643
- const isVisible = Boolean(visibleFrames[frameIndex]);
828
+ if (isFrameSpan(child)) {
829
+ const currentFrameIndex = frameIndex;
830
+ const isVisible = Boolean(visibleFrames[currentFrameIndex]);
644
831
  const shouldRenderHast = shouldHighlight && isVisible;
645
832
  frameIndex += 1;
646
- return /*#__PURE__*/_jsx("span", {
833
+
834
+ // Inject the variant-swap bridge inside the chosen frame. JSX
835
+ // siblings to `renderCode(...)` mean the host CSS — which
836
+ // animates `.frame .collapse > span` — still matches the
837
+ // placeholder without us mutating the underlying hast.
838
+ const bridgeNode = bridge && bridge.frameIndex === currentFrameIndex ? /*#__PURE__*/_jsx("span", {
839
+ className: "collapse",
840
+ "data-lines": bridge.lines,
841
+ children: Array.from({
842
+ length: bridge.lines
843
+ }, (_, i) => /*#__PURE__*/_jsx("span", {}, i))
844
+ }) : null;
845
+ return /*#__PURE__*/_jsxs("span", {
647
846
  className: "frame",
648
847
  "data-lined": shouldRenderHast ? '' : undefined,
649
- "data-frame-type": child.properties.dataFrameType ? String(child.properties.dataFrameType) : undefined,
848
+ "data-frame-type": resolveFrameTypeAttribute(child.properties.dataFrameType ? String(child.properties.dataFrameType) : undefined, collapseToEmpty),
650
849
  "data-frame-indent": child.properties.dataFrameIndent != null ? String(child.properties.dataFrameIndent) : undefined,
651
850
  "data-frame-truncated": child.properties.dataFrameTruncated ? String(child.properties.dataFrameTruncated) : undefined,
652
851
  "data-frame-description": child.properties.dataFrameDescription ? String(child.properties.dataFrameDescription) : undefined,
653
852
  ref: observeFrame,
654
- children: renderCode(child.children, shouldRenderHast, child.properties?.dataAsString ? String(child.properties?.dataAsString) : undefined)
853
+ children: [renderCode(child.children, shouldRenderHast, child.data?.fallback), bridgeNode]
655
854
  }, index);
656
855
  }
657
856
  return /*#__PURE__*/_jsx(React.Fragment, {
658
- children: shouldHighlight ? hastToJsx(child) : toText(child, {
659
- whitespace: 'pre'
660
- })
857
+ children: shouldHighlight ? hastToJsx(child) : renderFallbackChild(child)
661
858
  }, index);
662
859
  });
663
- }, [hast, observeFrame, shouldHighlight, visibleFrames]);
664
- const hasCollapsibleFrames = hast?.data?.collapsible === true;
860
+ }, [hast, bridge, observeFrame, shouldHighlight, visibleFrames, collapseToEmpty]);
861
+ const hasCollapsibleFrames = (hast ? getSourceLineCounts(hast).collapsible : fallbackLineCounts?.collapsible === true) || collapseToEmpty;
862
+
863
+ // Expose the source line counts so consumers / CSS can reason about the
864
+ // collapsed window size — most notably the collapse-to-nothing case
865
+ // (`focusedLines === 0`) produced by `oversizedFocus: 'hide'`, where the
866
+ // block is collapsible but the collapsed window is empty. Counts come from
867
+ // the parsed `hast` when available. Deferred string sources use the metadata
868
+ // that travelled with their framed fallback; without it, they fall back to the
869
+ // raw string count and remain non-collapsible.
870
+ const {
871
+ totalLines: sourceTotalLines,
872
+ focusedLines: rawFocusedLines
873
+ } = hast ? getSourceLineCounts(hast) : fallbackLineCounts ?? getSourceLineCounts(children);
874
+ // Collapse-to-empty empties the collapsed window, so the focused-line count is 0
875
+ // regardless of the precomputed value.
876
+ const sourceFocusedLines = collapseToEmpty ? 0 : rawFocusedLines;
665
877
  const isEditable = Boolean(setSource);
666
878
 
667
879
  // Focus-trap state for editable code blocks. When the user tabs into the
@@ -749,6 +961,43 @@ export function Pre({
749
961
  wrapperRef.current?.focus();
750
962
  }
751
963
  }, [setPromptVisible]);
964
+
965
+ // A plain-string source hasn't been highlighted yet (deferred mode). Render it
966
+ // FRAMED — the compact `fallback` (the loader's windowed plain-text frames) when one
967
+ // travelled with it, otherwise a single-frame wrap — so the `<code>` is never bare
968
+ // text. The highlighted tree swaps in via `frames` once parsing completes.
969
+ // The fallback render, used whenever the decoded `hast` isn't in hand: a string
970
+ // source (highlighted on the client after hydration) or an object source whose
971
+ // decode is deferred off the first paint (`deferInitialDecode`). For `init` the
972
+ // server-built `fallback` already carries the initially-visible frames
973
+ // highlighted, so this first paint is highlighted with no decompression.
974
+ const framedFallback = React.useMemo(() => {
975
+ const frameNodes = fallback ?? (typeof children === 'string' ? [['span', 'frame', {
976
+ dataFrameType: 'focus'
977
+ }, children]] : null);
978
+ if (!frameNodes) {
979
+ return null;
980
+ }
981
+ const root = fallbackToHast(frameNodes);
982
+ if (collapseToEmpty) {
983
+ for (const child of root.children) {
984
+ if (child.type !== 'element' || !isFrameSpan(child)) {
985
+ continue;
986
+ }
987
+ const frameType = typeof child.properties.dataFrameType === 'string' ? child.properties.dataFrameType : undefined;
988
+ const resolved = resolveFrameTypeAttribute(frameType, true);
989
+ if (resolved === frameType) {
990
+ continue;
991
+ }
992
+ if (!resolved) {
993
+ delete child.properties.dataFrameType;
994
+ } else {
995
+ child.properties.dataFrameType = resolved;
996
+ }
997
+ }
998
+ }
999
+ return hastToJsx(root);
1000
+ }, [children, collapseToEmpty, fallback]);
752
1001
  const preElement =
753
1002
  /*#__PURE__*/
754
1003
  // The <pre> is made interactive by contentEditable (set imperatively by
@@ -760,10 +1009,13 @@ export function Pre({
760
1009
  spellCheck: false,
761
1010
  tabIndex: isEditable ? -1 : undefined,
762
1011
  onKeyDown: isEditable ? handlePreKeyDown : undefined,
1012
+ "data-transforming": transforming ?? undefined,
763
1013
  children: /*#__PURE__*/_jsx("code", {
764
1014
  className: language ? `language-${language}` : undefined,
765
1015
  "data-collapsible": hasCollapsibleFrames ? '' : undefined,
766
- children: typeof children === 'string' ? children : frames
1016
+ "data-total-lines": sourceTotalLines,
1017
+ "data-focused-lines": sourceFocusedLines,
1018
+ children: hast ? frames : framedFallback
767
1019
  })
768
1020
  });
769
1021
  if (!isEditable) {