@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.
- package/ChunkProvider/ChunkContext.d.mts +10 -0
- package/ChunkProvider/ChunkContext.mjs +15 -0
- package/ChunkProvider/ChunkProvider.d.mts +14 -0
- package/ChunkProvider/ChunkProvider.mjs +38 -0
- package/ChunkProvider/PreloadContext.d.mts +14 -0
- package/ChunkProvider/PreloadContext.mjs +18 -0
- package/ChunkProvider/PreloadProvider.d.mts +13 -0
- package/ChunkProvider/PreloadProvider.mjs +33 -0
- package/ChunkProvider/index.d.mts +7 -0
- package/ChunkProvider/index.mjs +7 -0
- package/ChunkProvider/types.d.mts +23 -0
- package/ChunkProvider/types.mjs +1 -0
- package/ChunkProvider/usePreload.d.mts +8 -0
- package/ChunkProvider/usePreload.mjs +21 -0
- package/CodeControllerContext/CodeControllerContext.d.mts +11 -0
- package/CodeControllerContext/CodeControllerContext.mjs +2 -1
- package/CodeHighlighter/CodeHighlighter.d.mts +15 -1
- package/CodeHighlighter/CodeHighlighter.mjs +97 -319
- package/CodeHighlighter/CodeHighlighterChunk.d.mts +42 -0
- package/CodeHighlighter/CodeHighlighterChunk.mjs +77 -0
- package/CodeHighlighter/CodeHighlighterClient.mjs +597 -128
- package/CodeHighlighter/CodeHighlighterContext.d.mts +57 -1
- package/CodeHighlighter/CodeHighlighterFallbackContext.d.mts +14 -2
- package/CodeHighlighter/CodeHighlighterFallbackContext.mjs +1 -3
- package/CodeHighlighter/CodeInitialSourceLoader.d.mts +10 -0
- package/CodeHighlighter/CodeInitialSourceLoader.mjs +108 -0
- package/CodeHighlighter/CodeSourceLoader.d.mts +11 -0
- package/CodeHighlighter/CodeSourceLoader.mjs +128 -0
- package/CodeHighlighter/buildCodeHighlighterChunkProps.d.mts +47 -0
- package/CodeHighlighter/buildCodeHighlighterChunkProps.mjs +61 -0
- package/CodeHighlighter/buildStringFallback.d.mts +29 -0
- package/CodeHighlighter/buildStringFallback.mjs +42 -0
- package/CodeHighlighter/codeToFallbackProps.d.mts +31 -2
- package/CodeHighlighter/codeToFallbackProps.mjs +347 -42
- package/CodeHighlighter/createClientProps.d.mts +17 -0
- package/CodeHighlighter/createClientProps.mjs +78 -0
- package/CodeHighlighter/errors.d.mts +6 -0
- package/CodeHighlighter/errors.mjs +10 -0
- package/CodeHighlighter/fallbackCompression.d.mts +96 -0
- package/CodeHighlighter/fallbackCompression.mjs +253 -0
- package/CodeHighlighter/fallbackFormat.d.mts +137 -0
- package/CodeHighlighter/fallbackFormat.mjs +422 -0
- package/CodeHighlighter/index.d.mts +4 -1
- package/CodeHighlighter/index.mjs +3 -1
- package/CodeHighlighter/mergeComments.d.mts +38 -0
- package/CodeHighlighter/mergeComments.mjs +80 -0
- package/CodeHighlighter/prepareInitialSource.d.mts +42 -0
- package/CodeHighlighter/prepareInitialSource.mjs +292 -0
- package/CodeHighlighter/resolveFallbackCritical.d.mts +23 -0
- package/CodeHighlighter/resolveFallbackCritical.mjs +44 -0
- package/CodeHighlighter/types.d.mts +272 -8
- package/CodeHighlighter/useCodeFallback.d.mts +94 -0
- package/CodeHighlighter/useCodeFallback.mjs +204 -0
- package/CodeHighlighter/useGrammarsReady.d.mts +18 -0
- package/CodeHighlighter/useGrammarsReady.mjs +45 -0
- package/CodeHighlighter/useSpeculativeCodePreload.d.mts +26 -0
- package/CodeHighlighter/useSpeculativeCodePreload.mjs +40 -0
- package/CodeHighlighter/useSpeculativeEditingPreload.d.mts +33 -0
- package/CodeHighlighter/useSpeculativeEditingPreload.mjs +58 -0
- package/CodeHighlighter/useSpeculativeGrammarPreload.d.mts +23 -0
- package/CodeHighlighter/useSpeculativeGrammarPreload.mjs +31 -0
- package/CodeHighlighter/useSpeculativeUseCodePreload.d.mts +22 -0
- package/CodeHighlighter/useSpeculativeUseCodePreload.mjs +41 -0
- package/CodeProvider/CodeContext.d.mts +47 -12
- package/CodeProvider/CodeContext.mjs +7 -0
- package/CodeProvider/CodeProvider.d.mts +4 -2
- package/CodeProvider/CodeProvider.mjs +40 -102
- package/CodeProvider/CodeProviderLazy.d.mts +40 -0
- package/CodeProvider/CodeProviderLazy.mjs +96 -0
- package/CodeProvider/constants.d.mts +26 -0
- package/CodeProvider/constants.mjs +24 -0
- package/CodeProvider/createParseSourceWorkerClient.d.mts +6 -0
- package/CodeProvider/createParseSourceWorkerClient.mjs +22 -2
- package/CodeProvider/index.d.mts +2 -1
- package/CodeProvider/index.mjs +9 -1
- package/CodeProvider/parseSourceWorker.mjs +33 -0
- package/CodeProvider/useCodeProviderValue.d.mts +54 -0
- package/CodeProvider/useCodeProviderValue.mjs +188 -0
- package/CoordinatedLazy/ChunkServerLoader.d.mts +25 -0
- package/CoordinatedLazy/ChunkServerLoader.mjs +97 -0
- package/CoordinatedLazy/CoordinatedContentContext.d.mts +15 -0
- package/CoordinatedLazy/CoordinatedContentContext.mjs +22 -0
- package/CoordinatedLazy/CoordinatedFallbackContext.d.mts +11 -0
- package/CoordinatedLazy/CoordinatedFallbackContext.mjs +13 -0
- package/CoordinatedLazy/CoordinatedGateContext.d.mts +14 -0
- package/CoordinatedLazy/CoordinatedGateContext.mjs +19 -0
- package/CoordinatedLazy/CoordinatedLazy.d.mts +14 -0
- package/CoordinatedLazy/CoordinatedLazy.mjs +86 -0
- package/CoordinatedLazy/CoordinatedLazyClient.d.mts +24 -0
- package/CoordinatedLazy/CoordinatedLazyClient.mjs +65 -0
- package/CoordinatedLazy/LazyContent.d.mts +26 -0
- package/CoordinatedLazy/LazyContent.mjs +80 -0
- package/CoordinatedLazy/LazyContentServer.d.mts +18 -0
- package/CoordinatedLazy/LazyContentServer.mjs +25 -0
- package/CoordinatedLazy/buildChunkRenderInputs.d.mts +8 -0
- package/CoordinatedLazy/buildChunkRenderInputs.mjs +35 -0
- package/CoordinatedLazy/createCoordinatedLazy.d.mts +32 -0
- package/CoordinatedLazy/createCoordinatedLazy.mjs +127 -0
- package/CoordinatedLazy/index.d.mts +14 -0
- package/CoordinatedLazy/index.mjs +18 -0
- package/CoordinatedLazy/resolveChunkRender.d.mts +26 -0
- package/CoordinatedLazy/resolveChunkRender.mjs +73 -0
- package/CoordinatedLazy/types.d.mts +408 -0
- package/CoordinatedLazy/types.mjs +1 -0
- package/CoordinatedLazy/useChunk.d.mts +30 -0
- package/CoordinatedLazy/useChunk.mjs +135 -0
- package/CoordinatedLazy/useCoordinatedFallback.d.mts +12 -0
- package/CoordinatedLazy/useCoordinatedFallback.mjs +40 -0
- package/CoordinatedLazy/useCoordinatedSwap.d.mts +16 -0
- package/CoordinatedLazy/useCoordinatedSwap.mjs +124 -0
- package/LICENSE +1 -1
- package/abstractCreateDemo/abstractCreateDemo.d.mts +54 -3
- package/abstractCreateDemo/abstractCreateDemo.mjs +47 -7
- package/abstractCreateDemo/resolveDemoFlag.d.mts +20 -0
- package/abstractCreateDemo/resolveDemoFlag.mjs +25 -0
- package/abstractCreateStream/abstractCreateStream.d.mts +18 -0
- package/abstractCreateStream/abstractCreateStream.mjs +45 -0
- package/abstractCreateStream/index.d.mts +2 -0
- package/abstractCreateStream/index.mjs +1 -0
- package/abstractCreateStream/types.d.mts +34 -0
- package/abstractCreateStream/types.mjs +1 -0
- package/abstractCreateTypes/TypeCode.mjs +12 -11
- package/abstractCreateTypes/typesToJsx.mjs +30 -9
- package/cli/ensureDemoClients.mjs +4 -148
- package/cli/ensureDemoPages.d.mts +45 -0
- package/cli/ensureDemoPages.mjs +99 -0
- package/cli/fileUtils/index.d.mts +11 -0
- package/cli/fileUtils/index.mjs +48 -0
- package/cli/findDemoIndexFiles.d.mts +15 -0
- package/cli/findDemoIndexFiles.mjs +121 -0
- package/cli/index.mjs +1 -1
- package/cli/loadNextConfig.d.mts +25 -0
- package/cli/loadNextConfig.mjs +60 -1
- package/cli/runBrowser.mjs +1 -1
- package/cli/runValidate.mjs +44 -1
- package/package.json +84 -4
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasis.mjs +30 -0
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.d.mts +17 -0
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.mjs +52 -0
- package/pipeline/hastUtils/frameFallbackFromSpans.d.mts +18 -0
- package/pipeline/hastUtils/frameFallbackFromSpans.mjs +24 -0
- package/pipeline/hastUtils/hast.d.mts +27 -0
- package/pipeline/hastUtils/hastCompression.d.mts +3 -1
- package/pipeline/hastUtils/hastCompression.mjs +9 -1
- package/pipeline/hastUtils/hastDecompress.mjs +10 -4
- package/pipeline/hastUtils/hastDictionary.mjs +9 -0
- package/pipeline/hastUtils/hastUtils.d.mts +4 -3
- package/pipeline/hastUtils/hastUtils.mjs +24 -12
- package/pipeline/hastUtils/index.d.mts +2 -1
- package/pipeline/hastUtils/index.mjs +2 -1
- package/pipeline/hastUtils/stripHighlightingSpans.d.mts +6 -2
- package/pipeline/hastUtils/stripHighlightingSpans.mjs +22 -10
- package/pipeline/lintJavascriptDemoFocus/lintJavascriptDemoFocus.mjs +10 -7
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.d.mts +31 -13
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.mjs +50 -55
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.d.mts +78 -0
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs +405 -0
- package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.d.mts +5 -5
- package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.mjs +36 -66
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.d.mts +23 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs +92 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSource.d.mts +19 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSource.mjs +25 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.d.mts +17 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.mjs +26 -0
- package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +26 -2
- package/pipeline/loadIsomorphicCodeVariant/diffHast.mjs +563 -19
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.d.mts +49 -0
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.mjs +152 -0
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.d.mts +51 -0
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.mjs +161 -0
- package/pipeline/loadIsomorphicCodeVariant/flattenCodeVariant.mjs +6 -3
- package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.d.mts +12 -0
- package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.mjs +44 -0
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.d.mts +16 -0
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +74 -0
- package/pipeline/loadIsomorphicCodeVariant/loadCodeFallback.mjs +17 -5
- package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +229 -15
- package/pipeline/loadIsomorphicCodeVariant/transformSource.d.mts +2 -2
- package/pipeline/loadIsomorphicCodeVariant/transformSource.mjs +56 -22
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.d.mts +18 -0
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.mjs +11 -7
- package/pipeline/loadServerTypes/hastTypeUtils.d.mts +2 -2
- package/pipeline/loadServerTypes/hastTypeUtils.mjs +4 -4
- package/pipeline/loadServerTypes/loadServerTypes.mjs +1 -1
- package/pipeline/loadServerTypesMeta/extractJSDocText.d.mts +14 -0
- package/pipeline/loadServerTypesMeta/extractJSDocText.mjs +60 -0
- package/pipeline/loadServerTypesMeta/processTypes.mjs +43 -46
- package/pipeline/loadServerTypesText/order.mjs +1 -1
- package/pipeline/loadServerTypesText/parseTypesMarkdown.mjs +3 -1
- package/pipeline/loaderUtils/index.d.mts +0 -1
- package/pipeline/loaderUtils/index.mjs +0 -1
- package/pipeline/loaderUtils/parseImportsAndComments.d.mts +5 -1
- package/pipeline/loaderUtils/parseImportsAndComments.mjs +19 -9
- package/pipeline/loaderUtils/resolveModulePath.mjs +23 -1
- package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.d.mts +12 -0
- package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.mjs +17 -13
- package/pipeline/parseSource/addLineGutters.mjs +45 -11
- package/pipeline/parseSource/calculateFrameRanges.d.mts +22 -0
- package/pipeline/parseSource/calculateFrameRanges.mjs +69 -25
- package/pipeline/parseSource/detectGrammarScopes.d.mts +13 -0
- package/pipeline/parseSource/detectGrammarScopes.mjs +35 -0
- package/pipeline/parseSource/extendSyntaxTokens.mjs +501 -43
- package/pipeline/parseSource/frameVisibility.d.mts +47 -0
- package/pipeline/parseSource/frameVisibility.mjs +114 -0
- package/pipeline/parseSource/grammarCache.d.mts +33 -0
- package/pipeline/parseSource/grammarCache.mjs +73 -0
- package/pipeline/parseSource/grammarLoaders.d.mts +14 -0
- package/pipeline/parseSource/grammarLoaders.mjs +24 -0
- package/pipeline/parseSource/grammarMaps.d.mts +21 -1
- package/pipeline/parseSource/grammarMaps.mjs +36 -0
- package/pipeline/parseSource/isFrameSpan.d.mts +19 -0
- package/pipeline/parseSource/isFrameSpan.mjs +24 -0
- package/pipeline/parseSource/parseSource.d.mts +41 -6
- package/pipeline/parseSource/parseSource.mjs +184 -36
- package/pipeline/parseSource/redistributeFrameFallbacks.d.mts +40 -0
- package/pipeline/parseSource/redistributeFrameFallbacks.mjs +138 -0
- package/pipeline/parseSource/restructureFrames.d.mts +5 -0
- package/pipeline/parseSource/restructureFrames.mjs +179 -16
- package/pipeline/syncPageIndex/metadataToMarkdown.mjs +6 -2
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.d.mts +26 -0
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs +181 -114
- package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.d.mts +12 -0
- package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.mjs +52 -0
- package/pipeline/transformHtmlCodeInline/transformHtmlCodeInline.mjs +22 -1
- package/pipeline/transformTypescriptToJavascript/removeTypes.d.mts +5 -8
- package/pipeline/transformTypescriptToJavascript/removeTypes.mjs +27 -93
- package/useCode/EditableEngine.d.mts +233 -0
- package/useCode/EditableEngine.mjs +1712 -0
- package/useCode/EditingEngine.d.mts +13 -0
- package/useCode/EditingEngine.mjs +14 -0
- package/useCode/Pre.browser.mjs +5 -1
- package/useCode/Pre.d.mts +127 -1
- package/useCode/Pre.mjs +417 -165
- package/useCode/SourceEditingEngine.d.mts +50 -0
- package/useCode/SourceEditingEngine.mjs +461 -0
- package/useCode/TransformEngine.d.mts +39 -0
- package/useCode/TransformEngine.mjs +208 -0
- package/useCode/editingEngineCache.d.mts +29 -0
- package/useCode/editingEngineCache.mjs +68 -0
- package/useCode/sourceLineCounts.d.mts +80 -0
- package/useCode/sourceLineCounts.mjs +284 -0
- package/useCode/subscribeToggleNudge.d.mts +3 -0
- package/useCode/subscribeToggleNudge.mjs +95 -0
- package/useCode/transformEngineCache.d.mts +21 -0
- package/useCode/transformEngineCache.mjs +60 -0
- package/useCode/useCode.d.mts +140 -1
- package/useCode/useCode.mjs +250 -19
- package/useCode/useCodeUtils.d.mts +131 -20
- package/useCode/useCodeUtils.mjs +267 -194
- package/useCode/useCopyFunctionality.d.mts +13 -1
- package/useCode/useCopyFunctionality.mjs +39 -9
- package/useCode/useEditable.browser.mjs +10 -2
- package/useCode/useEditable.d.mts +27 -106
- package/useCode/useEditable.integration.browser.d.mts +1 -0
- package/useCode/useEditable.integration.browser.mjs +870 -0
- package/useCode/useEditable.mjs +198 -1247
- package/useCode/useEditableUtils.d.mts +50 -1
- package/useCode/useEditableUtils.mjs +29 -0
- package/useCode/useFileNavigation.d.mts +91 -3
- package/useCode/useFileNavigation.mjs +201 -41
- package/useCode/useHighlightGate.d.mts +17 -0
- package/useCode/useHighlightGate.mjs +147 -0
- package/useCode/useSourceEditing.d.mts +8 -0
- package/useCode/useSourceEditing.mjs +158 -314
- package/useCode/useSourceEnhancing.d.mts +5 -1
- package/useCode/useSourceEnhancing.mjs +22 -36
- package/useCode/useTransformManagement.d.mts +93 -5
- package/useCode/useTransformManagement.mjs +496 -28
- package/useCode/useTransitionPhase.d.mts +24 -0
- package/useCode/useTransitionPhase.mjs +49 -0
- package/useCode/useUIState.d.mts +2 -2
- package/useCode/useUIState.mjs +8 -8
- package/useCode/useVariantSelection.d.mts +130 -6
- package/useCode/useVariantSelection.mjs +529 -93
- package/useCodeWindow/useCodeWindow.d.mts +19 -2
- package/useCodeWindow/useCodeWindow.mjs +98 -71
- package/useCoordinated/coordinatePreference.d.mts +439 -0
- package/useCoordinated/coordinatePreference.mjs +951 -0
- package/useCoordinated/coordinatePreference.testUtils.d.mts +21 -0
- package/useCoordinated/coordinatePreference.testUtils.mjs +69 -0
- package/useCoordinated/createSettleGate.d.mts +96 -0
- package/useCoordinated/createSettleGate.mjs +171 -0
- package/useCoordinated/index.d.mts +8 -0
- package/useCoordinated/index.mjs +8 -0
- package/useCoordinated/layoutShiftGate.d.mts +24 -0
- package/useCoordinated/layoutShiftGate.mjs +79 -0
- package/useCoordinated/pageSettleGate.d.mts +11 -0
- package/useCoordinated/pageSettleGate.mjs +13 -0
- package/useCoordinated/scheduleTasks.d.mts +23 -0
- package/useCoordinated/scheduleTasks.mjs +45 -0
- package/useCoordinated/useCoordinated.d.mts +193 -0
- package/useCoordinated/useCoordinated.mjs +469 -0
- package/useCoordinated/useCoordinatedLazy.d.mts +17 -0
- package/useCoordinated/useCoordinatedLazy.mjs +38 -0
- package/useCoordinated/useCoordinatedLocalStorage.d.mts +16 -0
- package/useCoordinated/useCoordinatedLocalStorage.mjs +22 -0
- package/useCoordinated/useCoordinatedPreference.d.mts +20 -0
- package/useCoordinated/useCoordinatedPreference.mjs +26 -0
- package/useCoordinated/useSettleGate.d.mts +11 -0
- package/useCoordinated/useSettleGate.mjs +34 -0
- package/useDemo/exportVariant.d.mts +12 -5
- package/useDemo/exportVariant.mjs +59 -5
- package/useDemo/useDemo.d.mts +5 -2
- package/useScrollAnchor/useScrollAnchor.mjs +28 -5
- package/useStream/index.d.mts +6 -0
- package/useStream/index.mjs +6 -0
- package/useStream/streamChunks.d.mts +23 -0
- package/useStream/streamChunks.mjs +85 -0
- package/useStream/types.d.mts +45 -0
- package/useStream/types.mjs +1 -0
- package/useStream/useStream.d.mts +57 -0
- package/useStream/useStream.mjs +119 -0
- package/useStream/useStreamController.d.mts +15 -0
- package/useStream/useStreamController.mjs +90 -0
- package/withDocsInfra/withDocsInfra.d.mts +19 -0
- package/withDocsInfra/withDocsInfra.mjs +13 -5
- package/pipeline/loaderUtils/convertCommentsToOneIndexed.d.mts +0 -8
- 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,
|
|
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
|
|
17
|
+
const fallbackHastCache = new WeakMap();
|
|
12
18
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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' &&
|
|
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,
|
|
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
|
-
|
|
282
|
-
|
|
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
|
|
285
|
-
if (!
|
|
286
|
-
|
|
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
|
-
|
|
209
|
+
fallbackHastCache.set(hastChildren, jsx);
|
|
293
210
|
}
|
|
294
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
316
|
-
|
|
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
|
|
319
|
-
|
|
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
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
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
|
|
643
|
-
const
|
|
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
|
-
|
|
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.
|
|
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) :
|
|
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?.
|
|
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
|
-
|
|
1016
|
+
"data-total-lines": sourceTotalLines,
|
|
1017
|
+
"data-focused-lines": sourceFocusedLines,
|
|
1018
|
+
children: hast ? frames : framedFallback
|
|
767
1019
|
})
|
|
768
1020
|
});
|
|
769
1021
|
if (!isEditable) {
|