@mui/internal-docs-infra 0.11.1-canary.8 → 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 +85 -5
- 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
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
// `decodeHastSource` and `frameFallbackFromSpans` are already part of the
|
|
3
|
+
// always-loaded `useCode` shell (via `Pre`, `sourceLineCounts`,
|
|
4
|
+
// `useFileNavigation`, `useSourceEnhancing`). Passing them into the lazy
|
|
5
|
+
// transform engine keeps the engine chunk from statically pulling them (and
|
|
6
|
+
// `hastDecompress`) — they stay counted in this shell instead of being hoisted.
|
|
7
|
+
import { decodeHastSource } from "../pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs";
|
|
8
|
+
import { frameFallbackFromSpans } from "../pipeline/hastUtils/index.mjs";
|
|
9
|
+
import { getAvailableTransforms, getApplicableTransforms, transformHasCollapsePlaceholder } from "./useCodeUtils.mjs";
|
|
10
|
+
import { peekTransformEngine, loadTransformEngine, preloadTransformEngine, resetTransformEngineCache } from "./transformEngineCache.mjs";
|
|
11
|
+
import { useCodeContext } from "../CodeProvider/CodeContext.mjs";
|
|
3
12
|
import { usePreference } from "../usePreference/index.mjs";
|
|
13
|
+
import { useCoordinated } from "../useCoordinated/index.mjs";
|
|
14
|
+
import { useHighlightGate } from "./useHighlightGate.mjs";
|
|
15
|
+
import { useTransitionPhase } from "./useTransitionPhase.mjs";
|
|
16
|
+
|
|
17
|
+
// Stable identity for the hast helpers handed to the transform engine; both are
|
|
18
|
+
// module-level functions, so this never needs to change.
|
|
19
|
+
const transformRuntimeDeps = {
|
|
20
|
+
decode: decodeHastSource,
|
|
21
|
+
frameFallbackFromSpans
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// The transform applier (`createTransformedFiles`, which pulls the `jsondiffpatch`
|
|
25
|
+
// chunk) is loaded on demand and cached in the light `./transformEngineCache`
|
|
26
|
+
// module — shared so `CodeHighlighter`'s speculative preload can prime it before
|
|
27
|
+
// the first transform-bearing block renders. Re-exported for tests and warming.
|
|
28
|
+
export { preloadTransformEngine, resetTransformEngineCache };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Minimum coordinator barrier wait used when `transformDelay` is unset
|
|
32
|
+
* or zero but a layout-shift-prone swap still needs to land on the
|
|
33
|
+
* same frame as peer demos. One animation frame at ~60fps so the
|
|
34
|
+
* coordinated paint feels instantaneous but every peer commits
|
|
35
|
+
* together — otherwise multiple sibling demos on the page would each
|
|
36
|
+
* trigger their own layout shift in sequence.
|
|
37
|
+
*/
|
|
38
|
+
const MIN_TRANSFORM_WAIT_MS = 16;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Time after an originator's announce by which all peers should have
|
|
42
|
+
* acked their preload. The barrier fires `onWaitingForPeers` at this
|
|
43
|
+
* boundary so consumers can surface a transient loading indicator,
|
|
44
|
+
* but does NOT force-commit — the wait continues up to
|
|
45
|
+
* `ultimateTimeoutMs` (10s) for slow peers.
|
|
46
|
+
*/
|
|
47
|
+
const TRANSFORM_GRACE_PERIOD_MS = 300;
|
|
4
48
|
/**
|
|
5
49
|
* Hook for managing code transforms and their application
|
|
6
50
|
* Uses the useLocalStorage hook for local storage persistence of transform preferences
|
|
@@ -10,7 +54,11 @@ export function useTransformManagement({
|
|
|
10
54
|
effectiveCode,
|
|
11
55
|
selectedVariantKey,
|
|
12
56
|
selectedVariant,
|
|
13
|
-
initialTransform
|
|
57
|
+
initialTransform,
|
|
58
|
+
transformDelay,
|
|
59
|
+
transformLayoutShift,
|
|
60
|
+
selectedFileName,
|
|
61
|
+
expanded
|
|
14
62
|
}) {
|
|
15
63
|
// Transform state - get available transforms from context or from the effective code data
|
|
16
64
|
const availableTransforms = React.useMemo(() => {
|
|
@@ -23,46 +71,466 @@ export function useTransformManagement({
|
|
|
23
71
|
return getAvailableTransforms(effectiveCode, selectedVariantKey);
|
|
24
72
|
}, [context?.availableTransforms, effectiveCode, selectedVariantKey]);
|
|
25
73
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
74
|
+
// Lazily-resolved transform engine (the `jsondiffpatch`-pulling applier).
|
|
75
|
+
// Initialized synchronously from the module cache so a warmed block (a later
|
|
76
|
+
// block on the page, or a test pre-warm) builds transforms in the same commit.
|
|
77
|
+
const {
|
|
78
|
+
transformEngineLoader
|
|
79
|
+
} = useCodeContext();
|
|
80
|
+
const [transformEngine, setTransformEngine] = React.useState(() => peekTransformEngine() ?? null);
|
|
81
|
+
|
|
82
|
+
// Resolve the engine once the block actually has transforms. Read-only /
|
|
83
|
+
// no-transform blocks skip this entirely and never pull the chunk. Adopts a
|
|
84
|
+
// cache another block already warmed, otherwise loads via the accessor
|
|
85
|
+
// (deduped page-wide) or the built-in import. Fail open.
|
|
86
|
+
React.useEffect(() => {
|
|
87
|
+
if (transformEngine || availableTransforms.length === 0) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const warm = peekTransformEngine();
|
|
91
|
+
if (warm) {
|
|
92
|
+
// Adopt a sibling-warmed engine synchronously; the surrounding effect is a
|
|
93
|
+
// real async load. `peekTransformEngine()` is an impure read of a
|
|
94
|
+
// module-mutable cache, so this cannot be derived during render.
|
|
95
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
96
|
+
setTransformEngine(() => warm);
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
let cancelled = false;
|
|
100
|
+
// `preloadTransformEngine` caches the resolved applier (and DEBUG-logs a load
|
|
101
|
+
// failure), so we just read it back once it settles.
|
|
102
|
+
preloadTransformEngine(transformEngineLoader).then(() => {
|
|
103
|
+
if (cancelled) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const create = peekTransformEngine();
|
|
107
|
+
if (create) {
|
|
108
|
+
setTransformEngine(() => create);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return () => {
|
|
112
|
+
cancelled = true;
|
|
113
|
+
};
|
|
114
|
+
}, [transformEngine, availableTransforms.length, transformEngineLoader]);
|
|
115
|
+
|
|
116
|
+
// Broader set used to resolve a stored preference *and* to derive the
|
|
117
|
+
// localStorage key: includes rename-only transforms (manifest entries
|
|
118
|
+
// with `hasDelta: false`) so a user preference like 'js' still applies
|
|
119
|
+
// the `.ts` → `.js` rename even when the toggle is hidden because
|
|
120
|
+
// there's no source-level delta. We always compute this from
|
|
121
|
+
// `effectiveCode` — `context.availableTransforms` is the *visible*
|
|
122
|
+
// toggle list (filtered by `hasDelta`) and is intentionally not used
|
|
123
|
+
// here, otherwise rename-only entries would be dropped from
|
|
124
|
+
// resolution and the storage key would shift whenever a transform's
|
|
125
|
+
// visibility changed between sibling demos.
|
|
126
|
+
const applicableTransforms = React.useMemo(() => getApplicableTransforms(effectiveCode, selectedVariantKey), [effectiveCode, selectedVariantKey]);
|
|
127
|
+
|
|
128
|
+
// Coordinator key. Demos sharing the same applicable transform set
|
|
129
|
+
// belong to the same coordination group: a user click in one demo
|
|
130
|
+
// triggers a synchronized barrier across all of them. `null` only
|
|
131
|
+
// when there are no applicable transforms at all (nothing to swap),
|
|
132
|
+
// otherwise we always join — even single-transform demos benefit
|
|
133
|
+
// from coordinating with sibling instances that share the same
|
|
134
|
+
// toggle (e.g. an entire docs page of JS/TS-only demos).
|
|
135
|
+
const coordinatorKey = React.useMemo(() => applicableTransforms.length >= 1 ? [...applicableTransforms].sort().join(':') : null, [applicableTransforms]);
|
|
136
|
+
|
|
137
|
+
// Stable per-hook identity used by the coordinator to track which
|
|
138
|
+
// demos have acked the current barrier. `React.useId` gives us a
|
|
139
|
+
// unique-per-mount string without the impure `Math.random()` /
|
|
140
|
+
// `Date.now()` dance, and stays stable across re-renders.
|
|
141
|
+
const demoId = React.useId();
|
|
32
142
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
143
|
+
// Result of the off-critical-path `createTransformedFiles` call.
|
|
144
|
+
// Populated by `useCoordinated`'s `onCommit` so React batches the
|
|
145
|
+
// precomputed payload install with the committedValue flip into a
|
|
146
|
+
// single re-render in which the `transformedFiles` memo finds a
|
|
147
|
+
// matching cache entry and avoids re-running
|
|
148
|
+
// `createTransformedFiles` synchronously in the swap commit.
|
|
149
|
+
const [precomputed, setPrecomputed] = React.useState(null);
|
|
150
|
+
|
|
151
|
+
// Raw localStorage preference (string or empty/null encoding). The
|
|
152
|
+
// key is derived from `applicableTransforms` (the full set, includes
|
|
153
|
+
// rename-only entries) so demos with only rename-only transforms
|
|
154
|
+
// still participate in persistence and so a transform becoming
|
|
155
|
+
// rename-only doesn't move it to a different storage bucket.
|
|
156
|
+
const [storedValue, setStoredValue] = usePreference('transform', applicableTransforms.length === 1 ? applicableTransforms[0] : applicableTransforms,
|
|
157
|
+
// Don't use `initialTransform` as the fallback — localStorage
|
|
158
|
+
// should always take precedence. The initial-transform resolution
|
|
159
|
+
// happens below in `resolveTransform`.
|
|
160
|
+
() => null);
|
|
161
|
+
|
|
162
|
+
// Resolve a stored/initial value into a valid transform name (or null).
|
|
163
|
+
// Resolution uses `applicableTransforms` (which includes rename-only
|
|
164
|
+
// entries) so a stored preference can still apply a rename even when
|
|
165
|
+
// the toggle is hidden because no actual code delta exists.
|
|
166
|
+
const resolveTransform = React.useCallback(stored => {
|
|
167
|
+
if (stored !== null) {
|
|
168
|
+
if (stored === '') {
|
|
38
169
|
return null;
|
|
39
170
|
}
|
|
40
|
-
|
|
41
|
-
if (!availableTransforms.includes(storedValue)) {
|
|
171
|
+
if (!applicableTransforms.includes(stored)) {
|
|
42
172
|
return null;
|
|
43
173
|
}
|
|
44
|
-
return
|
|
174
|
+
return stored;
|
|
45
175
|
}
|
|
46
|
-
|
|
47
|
-
// If no stored value and we have an initial transform, use it (but don't store it yet)
|
|
48
|
-
if (initialTransform && availableTransforms.includes(initialTransform)) {
|
|
176
|
+
if (initialTransform && applicableTransforms.includes(initialTransform)) {
|
|
49
177
|
return initialTransform;
|
|
50
178
|
}
|
|
51
179
|
return null;
|
|
52
|
-
}, [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
180
|
+
}, [applicableTransforms, initialTransform]);
|
|
181
|
+
|
|
182
|
+
// While the highlighter has not yet produced parsed HAST for the
|
|
183
|
+
// transformed variant (`context.deferHighlight === true`), gate the
|
|
184
|
+
// localStorage-restored value behind the SSR-safe `null`. Without
|
|
185
|
+
// this gate, a refresh of a page whose stored preference selects a
|
|
186
|
+
// transform commits the swap during hydration — before
|
|
187
|
+
// `useCodeParsing` has run `parseCode` — so `<Pre>` paints the
|
|
188
|
+
// transformed source as unhighlighted plain text with a different
|
|
189
|
+
// frame structure, the collapse animation runs against that
|
|
190
|
+
// intermediate tree, and then `parseCode` resolves a frame or two
|
|
191
|
+
// later and the DOM rebuilds with full highlighting, producing a
|
|
192
|
+
// visible post-animation jump. Holding the underlying value at the
|
|
193
|
+
// SSR-safe default means `<Pre>` keeps rendering exactly what the
|
|
194
|
+
// server emitted until the highlighter is ready; the moment
|
|
195
|
+
// `deferHighlight` flips false, the receiver flow opens its barrier
|
|
196
|
+
// and the collapse animation plays once, against a fully-parsed
|
|
197
|
+
// target tree.
|
|
198
|
+
const effectiveStoredValue = context?.deferHighlight ? null : storedValue;
|
|
199
|
+
|
|
200
|
+
// Resolved view of the raw preference. This is the value
|
|
201
|
+
// `useCoordinated` sees as its "external source of truth"; when it
|
|
202
|
+
// changes from outside (peer broadcast, other tab, applicable
|
|
203
|
+
// transforms re-resolution, or the highlighter becoming ready) the
|
|
204
|
+
// hook's receiver flow opens a barrier so every demo on the page
|
|
205
|
+
// commits the swap together.
|
|
206
|
+
const resolvedStoredValue = React.useMemo(() => resolveTransform(effectiveStoredValue), [resolveTransform, effectiveStoredValue]);
|
|
207
|
+
|
|
208
|
+
// Wrap the storage setter so the `useCoordinated` tuple signature
|
|
209
|
+
// matches (`string | null` in, void out). `null` is encoded as `''`
|
|
210
|
+
// so the raw preference can distinguish "explicitly cleared"
|
|
211
|
+
// (empty string) from "never set / hydration placeholder" (null).
|
|
212
|
+
const setResolvedStoredValue = React.useCallback(next => {
|
|
213
|
+
setStoredValue(next === null ? '' : next);
|
|
56
214
|
}, [setStoredValue]);
|
|
57
215
|
|
|
58
|
-
//
|
|
216
|
+
// Stable tuple identity so `useCoordinated` doesn't churn its
|
|
217
|
+
// dependency arrays when this hook re-renders with the same value.
|
|
218
|
+
// The setter passed here is a no-op: storage writes are performed
|
|
219
|
+
// *eagerly* by `setSelectedTransformAsUser` (so user intent is
|
|
220
|
+
// persisted immediately on click and broadcast to peers in the same
|
|
221
|
+
// tick), not lazily on barrier commit. The engine sees the eager
|
|
222
|
+
// write echo back through `usePreference` and dedupes it via its
|
|
223
|
+
// `inFlightTargetRef` guard so the receiver flow doesn't double-fire.
|
|
224
|
+
const underlying = React.useMemo(() => [resolvedStoredValue, () => {}], [resolvedStoredValue]);
|
|
225
|
+
|
|
226
|
+
// Barrier wait length. Falls back to one frame when `transformDelay`
|
|
227
|
+
// isn't configured so peers still align on the same paint without
|
|
228
|
+
// making the click feel sluggish.
|
|
229
|
+
const hasDelay = typeof transformDelay === 'number' && transformDelay > 0;
|
|
230
|
+
|
|
231
|
+
// Latest committed transform — read by `causesLayoutShift` to
|
|
232
|
+
// classify the *outgoing* tree's collapse placeholders. Assigned
|
|
233
|
+
// after the `useCoordinated` call below so the ref always reflects
|
|
234
|
+
// the value the engine just committed.
|
|
235
|
+
const committedRef = React.useRef(resolvedStoredValue);
|
|
236
|
+
|
|
237
|
+
// Tracks the previous render's committed transform so we can decide
|
|
238
|
+
// the originator's `minWaitMs` synchronously inside
|
|
239
|
+
// `selectTransformDispatch`: leaving a non-null transform needs the
|
|
240
|
+
// pre-swap expand window, but `null → X` commits immediately.
|
|
241
|
+
const prevCommittedTransformRef = React.useRef(resolvedStoredValue);
|
|
242
|
+
|
|
243
|
+
// Latest props read by the engine's `causesLayoutShift` / `preload`
|
|
244
|
+
// / `onCommit` callbacks. Kept in a ref so those callbacks can be
|
|
245
|
+
// referentially stable (the engine captures them at announce time
|
|
246
|
+
// via the hook's internal callback ref, and a churn here would
|
|
247
|
+
// restart in-flight barriers).
|
|
248
|
+
const layoutShiftPropsRef = React.useRef({
|
|
249
|
+
selectedVariant,
|
|
250
|
+
transformLayoutShift,
|
|
251
|
+
selectedFileName,
|
|
252
|
+
expanded,
|
|
253
|
+
fallbacks: context?.fallbacks
|
|
254
|
+
});
|
|
255
|
+
// eslint-disable-next-line react-hooks/refs
|
|
256
|
+
layoutShiftPropsRef.current = {
|
|
257
|
+
selectedVariant,
|
|
258
|
+
transformLayoutShift,
|
|
259
|
+
selectedFileName,
|
|
260
|
+
expanded,
|
|
261
|
+
fallbacks: context?.fallbacks
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Plumb classifier props through `transformHasCollapsePlaceholder`
|
|
265
|
+
// on every render. The engine's `causesLayoutShift` callback is
|
|
266
|
+
// only invoked when a swap is actually announced, but consumers
|
|
267
|
+
// (and tests) rely on the classifier observing prop changes
|
|
268
|
+
// eagerly — both to validate plumbing and to surface any heavier
|
|
269
|
+
// diagnostic side effects the classifier might perform.
|
|
270
|
+
React.useMemo(() => transformHasCollapsePlaceholder(selectedVariant, resolvedStoredValue, {
|
|
271
|
+
mode: transformLayoutShift,
|
|
272
|
+
selectedFileName,
|
|
273
|
+
expanded
|
|
274
|
+
}), [selectedVariant, resolvedStoredValue, transformLayoutShift, selectedFileName, expanded]);
|
|
275
|
+
|
|
276
|
+
// A swap "causes layout shift" — and therefore needs the synchronous
|
|
277
|
+
// barrier path — when either the incoming or the outgoing tree
|
|
278
|
+
// carries `.collapse` placeholders that need to animate. Peers
|
|
279
|
+
// without layout shift get routed to the engine's lazy path and
|
|
280
|
+
// commit after `lazyMinWaitMs` (`effectiveDelay * 2` below) so their
|
|
281
|
+
// commit lands after the originator's full expand → swap → collapse
|
|
282
|
+
// window has played out.
|
|
283
|
+
const causesLayoutShift = React.useCallback(target => {
|
|
284
|
+
const props = layoutShiftPropsRef.current;
|
|
285
|
+
const incoming = transformHasCollapsePlaceholder(props.selectedVariant, target, {
|
|
286
|
+
mode: props.transformLayoutShift,
|
|
287
|
+
selectedFileName: props.selectedFileName,
|
|
288
|
+
expanded: props.expanded
|
|
289
|
+
});
|
|
290
|
+
if (incoming) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
return transformHasCollapsePlaceholder(props.selectedVariant, committedRef.current, {
|
|
294
|
+
mode: props.transformLayoutShift,
|
|
295
|
+
selectedFileName: props.selectedFileName,
|
|
296
|
+
expanded: props.expanded
|
|
297
|
+
});
|
|
298
|
+
}, []);
|
|
299
|
+
// Hold the originator's coordinator barrier open while the
|
|
300
|
+
// highlighter pipeline (sync `parseCode` + async `computeHastDeltas`)
|
|
301
|
+
// is still in flight. The receiver flow already masks the stored
|
|
302
|
+
// value through `effectiveStoredValue`, but an interactive click
|
|
303
|
+
// flows through `useCoordinated` directly and would otherwise commit
|
|
304
|
+
// after `transformDelay` even when `transformedCode` hasn't landed —
|
|
305
|
+
// painting the incoming tree from un-deltaed source then snapping to
|
|
306
|
+
// the deltaed version a frame later. See `useHighlightGate` for how
|
|
307
|
+
// the gate plumbs into the engine's `preload` slot.
|
|
308
|
+
const awaitHighlight = useHighlightGate(!!context?.deferHighlight);
|
|
309
|
+
|
|
310
|
+
// Off-critical-path build of the next file tree. Runs in the engine's
|
|
311
|
+
// `preload` slot so the originator's barrier holds open until the
|
|
312
|
+
// payload is ready, and the result is committed atomically with the
|
|
313
|
+
// value flip in `onCommit`.
|
|
314
|
+
//
|
|
315
|
+
// Synchronous fast path when the highlighter is ready: the engine
|
|
316
|
+
// fires the preload immediately (no microtask hop) so the result is
|
|
317
|
+
// available the moment `onCommit` runs inside the same `act(...)`
|
|
318
|
+
// callback as the timer fire.
|
|
319
|
+
//
|
|
320
|
+
// Async path when `context.deferHighlight === true`: we await the
|
|
321
|
+
// gate so the barrier's wait extends until the incoming tree can
|
|
322
|
+
// paint with its full transform deltas applied. The engine's
|
|
323
|
+
// `signal` is forwarded so a superseding announce can supersede the
|
|
324
|
+
// wait instead of leaking it.
|
|
325
|
+
const preload = React.useCallback((target, signal) => {
|
|
326
|
+
const buildResult = create => {
|
|
327
|
+
const props = layoutShiftPropsRef.current;
|
|
328
|
+
return {
|
|
329
|
+
variant: props.selectedVariant,
|
|
330
|
+
transform: target,
|
|
331
|
+
result: create(props.selectedVariant, target, transformRuntimeDeps, props.fallbacks)
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
// Resolve the engine: synchronously from the warm module cache, otherwise
|
|
335
|
+
// via the loader (defer-if-cold — the coordinator barrier holds the swap
|
|
336
|
+
// open until this promise resolves).
|
|
337
|
+
const wait = awaitHighlight(signal);
|
|
338
|
+
// Sync from the warm cache, else load (and cache) via the accessor.
|
|
339
|
+
const engine = loadTransformEngine(transformEngineLoader);
|
|
340
|
+
// Fully synchronous fast path: highlighter ready and engine already warm.
|
|
341
|
+
if (wait === null && typeof engine === 'function') {
|
|
342
|
+
return buildResult(engine);
|
|
343
|
+
}
|
|
344
|
+
return Promise.all([wait ?? Promise.resolve(), Promise.resolve(engine)]).then(([, create]) => buildResult(create));
|
|
345
|
+
}, [awaitHighlight, transformEngineLoader]);
|
|
346
|
+
const onCommit = React.useCallback((_target, preloaded) => {
|
|
347
|
+
if (preloaded) {
|
|
348
|
+
setPrecomputed(preloaded);
|
|
349
|
+
}
|
|
350
|
+
}, []);
|
|
351
|
+
const [delayedAppliedTransform, selectTransformDispatch, coordinationExtras] = useCoordinated(underlying, {
|
|
352
|
+
channelKey: coordinatorKey,
|
|
353
|
+
peerId: demoId,
|
|
354
|
+
causesLayoutShift,
|
|
355
|
+
preload,
|
|
356
|
+
onCommit,
|
|
357
|
+
// eslint-disable-next-line react-hooks/refs
|
|
358
|
+
minWaitMs: hasDelay && prevCommittedTransformRef.current !== null ? transformDelay : 0,
|
|
359
|
+
multiPeerExtraMinWaitMs: hasDelay ? 0 : MIN_TRANSFORM_WAIT_MS,
|
|
360
|
+
lazyMinWaitMs: hasDelay ? transformDelay : 0,
|
|
361
|
+
gracePeriodMs: TRANSFORM_GRACE_PERIOD_MS
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Keep the outgoing-tree probe in sync with whatever the engine just
|
|
365
|
+
// committed. Mutating a ref during render is safe — React tolerates
|
|
366
|
+
// it as long as the value derives deterministically from inputs of
|
|
367
|
+
// the current render.
|
|
368
|
+
// eslint-disable-next-line react-hooks/refs
|
|
369
|
+
committedRef.current = delayedAppliedTransform;
|
|
370
|
+
// eslint-disable-next-line react-hooks/refs
|
|
371
|
+
prevCommittedTransformRef.current = delayedAppliedTransform;
|
|
372
|
+
|
|
373
|
+
// User-facing "intent" value: updates synchronously on a local
|
|
374
|
+
// click (the engine sets `pendingValue` inside `runCoordination`
|
|
375
|
+
// before yielding) and on a peer broadcast (receiver flow likewise
|
|
376
|
+
// sets `pendingValue` synchronously when opening its barrier).
|
|
377
|
+
const selectedTransform = coordinationExtras.pendingValue;
|
|
378
|
+
|
|
379
|
+
// Surfaced by the engine only on the originator and only once the
|
|
380
|
+
// grace period has elapsed without convergence. `null` is a valid
|
|
381
|
+
// pending target (swap back to the un-transformed original);
|
|
382
|
+
// `undefined` means nothing is pending or we're still inside the
|
|
383
|
+
// grace window.
|
|
384
|
+
const pendingTransform = coordinationExtras.isWaitingForPeers ? coordinationExtras.pendingValue : undefined;
|
|
385
|
+
|
|
386
|
+
// No-op when called with the value already in flight / committed —
|
|
387
|
+
// otherwise the engine would open a fresh (redundant) barrier and
|
|
388
|
+
// re-announce, briefly toggling `isCoordinating` on peers. The
|
|
389
|
+
// storage write happens *before* the engine dispatch so user intent
|
|
390
|
+
// is broadcast to peer demos on the same tick as the click (every
|
|
391
|
+
// demo enters its expand → swap → collapse window together), even
|
|
392
|
+
// when this demo's own visible swap is gated by `transformDelay`.
|
|
393
|
+
//
|
|
394
|
+
// Validation differs from `resolveTransform`: an explicit `null`
|
|
395
|
+
// here means "user cleared the transform" — never re-resolved to
|
|
396
|
+
// `initialTransform` (which `resolveTransform` only consults for
|
|
397
|
+
// hydration of a never-set stored value).
|
|
398
|
+
const setSelectedTransformAsUser = React.useCallback(value => {
|
|
399
|
+
const resolved = value === null || applicableTransforms.includes(value) ? value : null;
|
|
400
|
+
if (resolved === selectedTransform) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
// Start local coordination from the user action first so this demo
|
|
404
|
+
// is always treated as the originator (which drives waiting affordances
|
|
405
|
+
// like pendingTransform), then broadcast the persisted preference.
|
|
406
|
+
selectTransformDispatch(resolved);
|
|
407
|
+
setResolvedStoredValue(resolved);
|
|
408
|
+
}, [applicableTransforms, selectedTransform, setResolvedStoredValue, selectTransformDispatch]);
|
|
409
|
+
|
|
410
|
+
// Post-swap `data-transforming` (`'expanded'` paused → `'collapsing'`
|
|
411
|
+
// active) window. Fires whenever the committed transform swaps to a
|
|
412
|
+
// non-null value:
|
|
413
|
+
//
|
|
414
|
+
// - `null → A` when A has no `.collapse` placeholders the
|
|
415
|
+
// barrier's `minWaitMs` already played out; this
|
|
416
|
+
// window is the only animation hook on the
|
|
417
|
+
// incoming tree.
|
|
418
|
+
// - `A → B` the pre-swap (`'collapsed'` → `'expanding'`)
|
|
419
|
+
// window opened by the barrier covered the
|
|
420
|
+
// outgoing tree; the post-swap window adds a
|
|
421
|
+
// matching trailing animation hook, giving
|
|
422
|
+
// transform-to-transform a `2 × transformDelay`
|
|
423
|
+
// total window (expand → swap → collapse) so
|
|
424
|
+
// consumer CSS can animate both the outgoing
|
|
425
|
+
// and the incoming tree.
|
|
426
|
+
// - `A → null` does not arm the window — the trailing
|
|
427
|
+
// untransformed tree has nothing to enter-
|
|
428
|
+
// animate.
|
|
429
|
+
//
|
|
430
|
+
// Detected during render so the flag lands on the same paint as the
|
|
431
|
+
// new tree, then cleared after `transformDelay` ms.
|
|
432
|
+
const [postSwapWindowActive, setPostSwapWindowActive] = React.useState(false);
|
|
433
|
+
// Seed with a sentinel (`undefined`) rather than the committed value so a
|
|
434
|
+
// transform that is ALREADY applied on the first render — restored from a
|
|
435
|
+
// saved localStorage preference, or a demo `initialTransform` default — reads
|
|
436
|
+
// as a null→X swap and arms the post-swap window, animating the swap the same
|
|
437
|
+
// way a manual toggle does. A null initial transform compares equal-to-null
|
|
438
|
+
// below (`delayedAppliedTransform !== null` is false), so a no-transform mount
|
|
439
|
+
// still does not animate.
|
|
440
|
+
const [prevAppliedTransform, setPrevAppliedTransform] = React.useState(undefined);
|
|
441
|
+
if (prevAppliedTransform !== delayedAppliedTransform) {
|
|
442
|
+
setPrevAppliedTransform(delayedAppliedTransform);
|
|
443
|
+
if (delayedAppliedTransform !== null && hasDelay) {
|
|
444
|
+
setPostSwapWindowActive(true);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// The window only ever opens under `hasDelay` (line above), so an open window
|
|
448
|
+
// when `!hasDelay` means `hasDelay` flipped true→false — a derivable invariant,
|
|
449
|
+
// not a side-effect. Clear it during render so it lands on the same commit.
|
|
450
|
+
if (postSwapWindowActive && !hasDelay) {
|
|
451
|
+
setPostSwapWindowActive(false);
|
|
452
|
+
}
|
|
453
|
+
React.useEffect(() => {
|
|
454
|
+
if (!postSwapWindowActive) {
|
|
455
|
+
return undefined;
|
|
456
|
+
}
|
|
457
|
+
// `delayedAppliedTransform` is in the dep array so a fresh swap
|
|
458
|
+
// during an already-open window (A → B → C in rapid succession)
|
|
459
|
+
// re-arms the timer for the full `transformDelay` instead of
|
|
460
|
+
// inheriting whatever was left over from B's window.
|
|
461
|
+
const timerId = setTimeout(() => setPostSwapWindowActive(false), transformDelay);
|
|
462
|
+
return () => clearTimeout(timerId);
|
|
463
|
+
// `hasDelay` is intentionally not a dependency: the body never reads it (the
|
|
464
|
+
// `!hasDelay` window teardown is the render-time clear above).
|
|
465
|
+
}, [postSwapWindowActive, transformDelay, delayedAppliedTransform]);
|
|
466
|
+
|
|
467
|
+
// If both phases are technically eligible (e.g. user clicked a third
|
|
468
|
+
// target during a post-swap window), the pending pre-swap takes
|
|
469
|
+
// priority — the visible tree IS the just-applied one and it needs
|
|
470
|
+
// to expand out for the next swap. When `transformDelay` is not
|
|
471
|
+
// configured, no animation window is opening (any coordinator wait
|
|
472
|
+
// is the one-frame `MIN_TRANSFORM_WAIT_MS`, too short to animate)
|
|
473
|
+
// so the phase stays `null` even if `delayedAppliedTransform`
|
|
474
|
+
// briefly lags `selectedTransform`.
|
|
475
|
+
//
|
|
476
|
+
// Each phase enters a "paused" value first (`'collapsed'` for the
|
|
477
|
+
// pre-swap window, `'expanded'` for the post-swap window). The
|
|
478
|
+
// rendered `<Pre>` calls `notifyTransformTransitionReady` once it
|
|
479
|
+
// has painted the new tree at that paused value, flipping
|
|
480
|
+
// `transformTransitionReady` to `true` which advances the phase to
|
|
481
|
+
// the matching active value (`'expanding'` / `'collapsing'`). The
|
|
482
|
+
// readiness flag is keyed on the current paused window so each new
|
|
483
|
+
// swap starts with a fresh wait.
|
|
484
|
+
const transformTransitionWindowKey = `${String(delayedAppliedTransform)}|${String(selectedTransform)}|${postSwapWindowActive ? '1' : '0'}`;
|
|
485
|
+
const {
|
|
486
|
+
ready: transformTransitionReady,
|
|
487
|
+
notify: notifyTransformTransitionReady
|
|
488
|
+
} = useTransitionPhase(transformTransitionWindowKey);
|
|
489
|
+
const transformingPhase = (() => {
|
|
490
|
+
if (!hasDelay) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
// `null -> transform` should never expose a pre-swap 'expanding' frame.
|
|
494
|
+
// During hydration restore, `pendingValue` can flip to the stored
|
|
495
|
+
// transform one render before commit; treating that as 'expanding' causes
|
|
496
|
+
// a visible double animation (expand then collapse). Only non-null
|
|
497
|
+
// outgoing trees need the pre-swap expand phase.
|
|
498
|
+
if (delayedAppliedTransform !== selectedTransform && delayedAppliedTransform !== null) {
|
|
499
|
+
return transformTransitionReady ? 'expanding' : 'collapsed';
|
|
500
|
+
}
|
|
501
|
+
if (postSwapWindowActive) {
|
|
502
|
+
return transformTransitionReady ? 'collapsing' : 'expanded';
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
})();
|
|
506
|
+
|
|
507
|
+
// Memoize all transformed files based on the *committed* transform
|
|
508
|
+
// so the rendered tree stays put during the `transformDelay` window.
|
|
509
|
+
// Prefer the precomputed result captured by `useCoordinated`'s
|
|
510
|
+
// `onCommit` when its `(variant, transform)` keys match the values
|
|
511
|
+
// about to be rendered.
|
|
59
512
|
const transformedFiles = React.useMemo(() => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
513
|
+
if (precomputed && precomputed.variant === selectedVariant && precomputed.transform === delayedAppliedTransform) {
|
|
514
|
+
return precomputed.result;
|
|
515
|
+
}
|
|
516
|
+
// The engine hasn't resolved yet (cold). Defer this render's build — the
|
|
517
|
+
// resolve effect re-renders once it's ready, and the (un-transformed)
|
|
518
|
+
// original files render for the one intervening tick. `createTransformedFiles`
|
|
519
|
+
// returns `undefined` for a null transform anyway, so a no-transform block
|
|
520
|
+
// (engine never loaded) correctly yields `undefined` here.
|
|
521
|
+
if (!transformEngine) {
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
return transformEngine(selectedVariant, delayedAppliedTransform, transformRuntimeDeps, context?.fallbacks);
|
|
525
|
+
}, [precomputed, selectedVariant, delayedAppliedTransform, context?.fallbacks, transformEngine]);
|
|
526
|
+
const result = {
|
|
63
527
|
availableTransforms,
|
|
64
528
|
selectedTransform,
|
|
65
529
|
transformedFiles,
|
|
66
|
-
selectTransform: setSelectedTransformAsUser
|
|
530
|
+
selectTransform: setSelectedTransformAsUser,
|
|
531
|
+
transformingPhase,
|
|
532
|
+
notifyTransformTransitionReady,
|
|
533
|
+
pendingTransform
|
|
67
534
|
};
|
|
535
|
+
return result;
|
|
68
536
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Four-state paused→active animation phase shared by
|
|
3
|
+
* `useTransformManagement` and `useVariantSelection`. Each swap
|
|
4
|
+
* window enters a "paused" value first (`'collapsed'` pre-swap or
|
|
5
|
+
* `'expanded'` post-swap) so the rendered `<Pre>` can settle into
|
|
6
|
+
* the animation start state; the host then advances to the matching
|
|
7
|
+
* active value (`'expanding'` / `'collapsing'`) once readiness fires.
|
|
8
|
+
*/
|
|
9
|
+
export type TransitionPhase = 'collapsed' | 'expanding' | 'expanded' | 'collapsing' | null;
|
|
10
|
+
/**
|
|
11
|
+
* Tracks the paused→active handshake for a single phase source.
|
|
12
|
+
*
|
|
13
|
+
* `windowKey` is an opaque identifier the caller composes from
|
|
14
|
+
* whatever uniquely identifies the current swap window (typically
|
|
15
|
+
* `(from, to, postSwap)`). The readiness flag is keyed against it so
|
|
16
|
+
* every new window starts with a fresh wait — `notify()` only marks
|
|
17
|
+
* the current window ready, and a subsequent window flip
|
|
18
|
+
* automatically falls back to "not ready" without any explicit
|
|
19
|
+
* reset.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useTransitionPhase(windowKey: string): {
|
|
22
|
+
ready: boolean;
|
|
23
|
+
notify: () => void;
|
|
24
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Four-state paused→active animation phase shared by
|
|
5
|
+
* `useTransformManagement` and `useVariantSelection`. Each swap
|
|
6
|
+
* window enters a "paused" value first (`'collapsed'` pre-swap or
|
|
7
|
+
* `'expanded'` post-swap) so the rendered `<Pre>` can settle into
|
|
8
|
+
* the animation start state; the host then advances to the matching
|
|
9
|
+
* active value (`'expanding'` / `'collapsing'`) once readiness fires.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tracks the paused→active handshake for a single phase source.
|
|
14
|
+
*
|
|
15
|
+
* `windowKey` is an opaque identifier the caller composes from
|
|
16
|
+
* whatever uniquely identifies the current swap window (typically
|
|
17
|
+
* `(from, to, postSwap)`). The readiness flag is keyed against it so
|
|
18
|
+
* every new window starts with a fresh wait — `notify()` only marks
|
|
19
|
+
* the current window ready, and a subsequent window flip
|
|
20
|
+
* automatically falls back to "not ready" without any explicit
|
|
21
|
+
* reset.
|
|
22
|
+
*/
|
|
23
|
+
export function useTransitionPhase(windowKey) {
|
|
24
|
+
const [state, setState] = React.useState({
|
|
25
|
+
key: windowKey,
|
|
26
|
+
ready: false
|
|
27
|
+
});
|
|
28
|
+
// React's recommended pattern for resetting state when an input
|
|
29
|
+
// changes: a guarded `setState` during render. This guarantees
|
|
30
|
+
// each new window starts at not-ready, even if the same key value
|
|
31
|
+
// recurs along an unusual swap path.
|
|
32
|
+
if (state.key !== windowKey) {
|
|
33
|
+
setState({
|
|
34
|
+
key: windowKey,
|
|
35
|
+
ready: false
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const ready = state.key === windowKey && state.ready;
|
|
39
|
+
const notify = React.useCallback(() => {
|
|
40
|
+
setState(prev => prev.key === windowKey ? {
|
|
41
|
+
key: windowKey,
|
|
42
|
+
ready: true
|
|
43
|
+
} : prev);
|
|
44
|
+
}, [windowKey]);
|
|
45
|
+
return {
|
|
46
|
+
ready,
|
|
47
|
+
notify
|
|
48
|
+
};
|
|
49
|
+
}
|
package/useCode/useUIState.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
interface UseUIStateProps {
|
|
3
|
-
|
|
3
|
+
initialExpanded?: boolean;
|
|
4
4
|
mainSlug?: string;
|
|
5
5
|
}
|
|
6
6
|
export interface UseUIStateResult {
|
|
@@ -13,7 +13,7 @@ export interface UseUIStateResult {
|
|
|
13
13
|
* Auto-expands if there's a relevant hash for this demo
|
|
14
14
|
*/
|
|
15
15
|
export declare function useUIState({
|
|
16
|
-
|
|
16
|
+
initialExpanded,
|
|
17
17
|
mainSlug
|
|
18
18
|
}: UseUIStateProps): UseUIStateResult;
|
|
19
19
|
export {};
|