@mui/internal-docs-infra 0.12.1-canary.2 → 0.12.1-canary.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/ChunkProvider/PreloadProvider.mjs +1 -2
  2. package/ChunkProvider/usePreload.d.mts +1 -1
  3. package/ChunkProvider/usePreload.mjs +0 -1
  4. package/CodeControllerContext/CodeControllerContext.d.mts +45 -10
  5. package/CodeControllerContext/CodeControllerContext.mjs +15 -1
  6. package/CodeHighlighter/CodeHighlighterClient.d.mts +3 -2
  7. package/CodeHighlighter/CodeHighlighterClient.mjs +113 -18
  8. package/CodeHighlighter/CodeHighlighterContext.d.mts +9 -3
  9. package/CodeHighlighter/buildStringFallback.d.mts +1 -1
  10. package/CodeHighlighter/codeToFallbackProps.d.mts +1 -1
  11. package/CodeHighlighter/createClientProps.mjs +3 -0
  12. package/CodeHighlighter/fallbackCompression.d.mts +1 -1
  13. package/CodeHighlighter/parseControlledCode.d.mts +13 -4
  14. package/CodeHighlighter/parseControlledCode.mjs +41 -49
  15. package/CodeHighlighter/types.d.mts +21 -0
  16. package/CodeHighlighter/useGrammarsReady.mjs +44 -9
  17. package/CodeProvider/CodeProvider.d.mts +1 -1
  18. package/CodeProvider/CodeProviderLazy.d.mts +1 -1
  19. package/CodeProvider/createParseSourceWorkerClient.d.mts +6 -0
  20. package/CodeProvider/createParseSourceWorkerClient.mjs +83 -15
  21. package/CodeProvider/useCodeProviderValue.mjs +50 -4
  22. package/abstractCreateDemo/abstractCreateDemo.d.mts +15 -1
  23. package/abstractCreateDemo/abstractCreateDemo.mjs +8 -0
  24. package/abstractCreateDemoClient/abstractCreateDemoClient.mjs +4 -0
  25. package/abstractCreateTypes/abstractCreateTypes.d.mts +12 -1
  26. package/abstractCreateTypes/abstractCreateTypes.mjs +4 -0
  27. package/abstractCreateTypes/typesToJsx.d.mts +8 -0
  28. package/abstractCreateTypes/typesToJsx.mjs +2 -1
  29. package/cli/loadNextConfig.d.mts +9 -0
  30. package/cli/loadNextConfig.mjs +11 -2
  31. package/cli/runValidate.mjs +15 -7
  32. package/cli/validateWorker.d.mts +1 -0
  33. package/cli/validateWorker.mjs +2 -1
  34. package/createDemoData/createDemoData.d.mts +1 -1
  35. package/package.json +34 -5
  36. package/pipeline/cacheUtils/constants.d.mts +5 -0
  37. package/pipeline/cacheUtils/constants.mjs +5 -0
  38. package/pipeline/cacheUtils/hashCacheContent.d.mts +5 -0
  39. package/pipeline/cacheUtils/hashCacheContent.mjs +9 -0
  40. package/pipeline/cacheUtils/index.d.mts +9 -0
  41. package/pipeline/cacheUtils/index.mjs +7 -0
  42. package/pipeline/cacheUtils/loadFileCache.d.mts +13 -0
  43. package/pipeline/cacheUtils/loadFileCache.mjs +20 -0
  44. package/pipeline/cacheUtils/loadFileCacheEntry.d.mts +13 -0
  45. package/pipeline/cacheUtils/loadFileCacheEntry.mjs +47 -0
  46. package/pipeline/cacheUtils/resolveCachePath.d.mts +16 -0
  47. package/pipeline/cacheUtils/resolveCachePath.mjs +24 -0
  48. package/pipeline/cacheUtils/saveFileCache.d.mts +17 -0
  49. package/pipeline/cacheUtils/saveFileCache.mjs +54 -0
  50. package/pipeline/cacheUtils/types.d.mts +23 -0
  51. package/pipeline/cacheUtils/types.mjs +1 -0
  52. package/pipeline/cacheUtils/withFileCache.d.mts +40 -0
  53. package/pipeline/cacheUtils/withFileCache.mjs +81 -0
  54. package/pipeline/hastUtils/hastUtils.d.mts +1 -1
  55. package/pipeline/hastUtils/hastUtils.mjs +0 -1
  56. package/pipeline/loadIsomorphicCodeSource/loadIsomorphicCodeSource.d.mts +1 -1
  57. package/pipeline/loadIsomorphicCodeSource/loadIsomorphicCodeSource.mjs +1 -1
  58. package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.mjs +0 -1
  59. package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +1 -1
  60. package/pipeline/loadIsomorphicCodeVariant/hasAllCodeVariants.d.mts +1 -1
  61. package/pipeline/loadIsomorphicCodeVariant/maybeCodeInitialData.d.mts +1 -1
  62. package/pipeline/loadPrecomputedSitemap/loadPrecomputedSitemap.d.mts +6 -0
  63. package/pipeline/loadPrecomputedSitemap/loadPrecomputedSitemap.mjs +2 -1
  64. package/pipeline/loadPrecomputedTypes/loadPrecomputedTypes.d.mts +6 -0
  65. package/pipeline/loadPrecomputedTypes/loadPrecomputedTypes.mjs +5 -1
  66. package/pipeline/loadServerCodeMeta/resolveModulePathWithFs.d.mts +1 -1
  67. package/pipeline/loadServerCodeMeta/resolveModulePathWithFs.mjs +0 -1
  68. package/pipeline/loadServerPageIndex/enrichPageIndex.d.mts +15 -0
  69. package/pipeline/loadServerPageIndex/enrichPageIndex.mjs +50 -0
  70. package/pipeline/loadServerPageIndex/extractPrefixAndTitle.d.mts +19 -0
  71. package/pipeline/loadServerPageIndex/extractPrefixAndTitle.mjs +80 -0
  72. package/pipeline/loadServerPageIndex/index.d.mts +5 -2
  73. package/pipeline/loadServerPageIndex/index.mjs +4 -1
  74. package/pipeline/loadServerPageIndex/loadServerPageIndex.d.mts +13 -22
  75. package/pipeline/loadServerPageIndex/loadServerPageIndex.mjs +32 -124
  76. package/pipeline/loadServerPageIndex/resolvePageIndexCacheKey.d.mts +13 -0
  77. package/pipeline/loadServerPageIndex/resolvePageIndexCacheKey.mjs +23 -0
  78. package/pipeline/loadServerTypes/highlightTypes.d.mts +3 -3
  79. package/pipeline/loadServerTypes/highlightTypesMeta.d.mts +4 -4
  80. package/pipeline/loadServerTypes/loadServerTypes.d.mts +2 -2
  81. package/pipeline/loadServerTypes/loadServerTypes.mjs +67 -7
  82. package/pipeline/loadServerTypesMeta/createOptimizedProgram.d.mts +2 -1
  83. package/pipeline/loadServerTypesMeta/formatClass.d.mts +2 -2
  84. package/pipeline/loadServerTypesMeta/formatComponent.d.mts +2 -2
  85. package/pipeline/loadServerTypesMeta/formatFunction.d.mts +2 -2
  86. package/pipeline/loadServerTypesMeta/formatHook.d.mts +2 -2
  87. package/pipeline/loadServerTypesMeta/formatRaw.d.mts +2 -2
  88. package/pipeline/loadServerTypesMeta/formatType.d.mts +1 -1
  89. package/pipeline/loadServerTypesMeta/loadServerTypesMeta.d.mts +7 -7
  90. package/pipeline/loadServerTypesMeta/loadServerTypesMeta.mjs +1 -1
  91. package/pipeline/loadServerTypesMeta/processTypes.d.mts +2 -2
  92. package/pipeline/loadServerTypesMeta/worker.d.mts +1 -1
  93. package/pipeline/loadServerTypesText/loadServerTypesText.d.mts +16 -2
  94. package/pipeline/loadServerTypesText/loadServerTypesText.mjs +30 -8
  95. package/pipeline/loadServerTypesText/normalizeTypesSourceDataForCache.d.mts +2 -0
  96. package/pipeline/loadServerTypesText/normalizeTypesSourceDataForCache.mjs +144 -0
  97. package/pipeline/loadServerTypesText/resolveTypesCacheKey.d.mts +19 -0
  98. package/pipeline/loadServerTypesText/resolveTypesCacheKey.mjs +29 -0
  99. package/pipeline/loaderUtils/parseImportsAndComments.d.mts +5 -3
  100. package/pipeline/loaderUtils/parseImportsAndComments.mjs +284 -40
  101. package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.d.mts +2 -2
  102. package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.mjs +2 -3
  103. package/pipeline/parseSource/detectFileTypes.d.mts +16 -0
  104. package/pipeline/parseSource/detectFileTypes.mjs +44 -0
  105. package/pipeline/syncPageIndex/mergeMetadataMarkdown.d.mts +25 -1
  106. package/pipeline/syncPageIndex/mergeMetadataMarkdown.mjs +79 -49
  107. package/pipeline/syncPageIndex/metadataToMarkdown.d.mts +15 -1
  108. package/pipeline/syncPageIndex/metadataToMarkdown.mjs +32 -10
  109. package/pipeline/syncPageIndex/syncPageIndex.d.mts +12 -0
  110. package/pipeline/syncPageIndex/syncPageIndex.mjs +154 -40
  111. package/pipeline/syncTypes/generateTypesMarkdown.d.mts +2 -2
  112. package/pipeline/syncTypes/syncTypes.d.mts +7 -0
  113. package/pipeline/syncTypes/syncTypes.mjs +31 -1
  114. package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs +1 -1
  115. package/pipeline/transformMarkdownCode/transformMarkdownCode.mjs +25 -15
  116. package/pipeline/transformMarkdownMetadata/transformMarkdownMetadata.mjs +3 -0
  117. package/pipeline/transformMarkdownMetadata/types.d.mts +8 -1
  118. package/useCode/EditableEngine.d.mts +1 -1
  119. package/useCode/Pre.d.mts +9 -2
  120. package/useCode/Pre.mjs +4 -3
  121. package/useCode/TransformEngine.d.mts +1 -1
  122. package/useCode/useCode.d.mts +17 -1
  123. package/useCode/useCode.mjs +22 -1
  124. package/useCode/useCopyFunctionality.d.mts +2 -2
  125. package/useCode/useEditable.browser.mjs +0 -1
  126. package/useCode/useFileNavigation.d.mts +3 -0
  127. package/useCode/useFileNavigation.mjs +3 -1
  128. package/useCode/useSourceEditing.mjs +45 -10
  129. package/useCode/useSourceEnhancing.mjs +41 -9
  130. package/useCode/useTransformManagement.d.mts +2 -2
  131. package/useCode/useTransformManagement.mjs +19 -3
  132. package/useCode/useUIState.d.mts +4 -0
  133. package/useCode/useUIState.mjs +8 -1
  134. package/useCode/useVariantSelection.d.mts +1 -1
  135. package/useCoordinated/useCoordinated.d.mts +1 -1
  136. package/useCoordinated/useCoordinatedLocalStorage.d.mts +1 -1
  137. package/useCoordinated/useCoordinatedLocalStorage.mjs +0 -1
  138. package/useCoordinated/useCoordinatedPreference.d.mts +1 -1
  139. package/useCoordinated/useCoordinatedPreference.mjs +0 -1
  140. package/useCrossTabState/index.d.mts +2 -0
  141. package/useCrossTabState/index.mjs +2 -0
  142. package/useCrossTabState/useCrossTabMirror.d.mts +26 -0
  143. package/useCrossTabState/useCrossTabMirror.mjs +108 -0
  144. package/useCrossTabState/useCrossTabState.d.mts +21 -0
  145. package/useCrossTabState/useCrossTabState.mjs +31 -0
  146. package/useDemo/exportVariant.mjs +2 -4
  147. package/useDemo/exportVariantAsCra.d.mts +1 -1
  148. package/useDemo/exportVariantAsCra.mjs +0 -1
  149. package/useDemo/useDemo.d.mts +4 -1
  150. package/useDemo/useDemo.mjs +5 -0
  151. package/useDemoController/BuildEngine.d.mts +2 -0
  152. package/useDemoController/BuildEngine.mjs +10 -0
  153. package/useDemoController/DemoRunner.d.mts +34 -0
  154. package/useDemoController/DemoRunner.mjs +83 -0
  155. package/useDemoController/ErrorBoundary.d.mts +34 -0
  156. package/useDemoController/ErrorBoundary.mjs +49 -0
  157. package/useDemoController/Runner.d.mts +44 -0
  158. package/useDemoController/Runner.mjs +71 -0
  159. package/useDemoController/absolutizeImports.d.mts +13 -0
  160. package/useDemoController/absolutizeImports.mjs +73 -0
  161. package/useDemoController/buildScope.d.mts +48 -0
  162. package/useDemoController/buildScope.mjs +270 -0
  163. package/useDemoController/collectSources.d.mts +67 -0
  164. package/useDemoController/collectSources.mjs +92 -0
  165. package/useDemoController/compileCssModule.d.mts +64 -0
  166. package/useDemoController/compileCssModule.mjs +40 -0
  167. package/useDemoController/compileCssWithPostcss.d.mts +17 -0
  168. package/useDemoController/compileCssWithPostcss.mjs +89 -0
  169. package/useDemoController/compileModule.d.mts +14 -0
  170. package/useDemoController/compileModule.mjs +17 -0
  171. package/useDemoController/constants.d.mts +13 -0
  172. package/useDemoController/constants.mjs +13 -0
  173. package/useDemoController/createRequire.d.mts +10 -0
  174. package/useDemoController/createRequire.mjs +17 -0
  175. package/useDemoController/createTranspileWorkerClient.d.mts +27 -0
  176. package/useDemoController/createTranspileWorkerClient.mjs +139 -0
  177. package/useDemoController/cssModules.d.mts +47 -0
  178. package/useDemoController/currentBrowserTarget.d.mts +16 -0
  179. package/useDemoController/currentBrowserTarget.mjs +36 -0
  180. package/useDemoController/evalCode.d.mts +12 -0
  181. package/useDemoController/evalCode.mjs +35 -0
  182. package/useDemoController/generateCssModuleSource.d.mts +11 -0
  183. package/useDemoController/generateCssModuleSource.mjs +55 -0
  184. package/useDemoController/generateElement.d.mts +21 -0
  185. package/useDemoController/generateElement.mjs +26 -0
  186. package/useDemoController/hashString.d.mts +11 -0
  187. package/useDemoController/hashString.mjs +20 -0
  188. package/useDemoController/importCode.d.mts +11 -0
  189. package/useDemoController/importCode.mjs +15 -0
  190. package/useDemoController/index.d.mts +1 -0
  191. package/useDemoController/index.mjs +1 -0
  192. package/useDemoController/instantiateElement.d.mts +24 -0
  193. package/useDemoController/instantiateElement.mjs +57 -0
  194. package/useDemoController/instantiateModule.d.mts +17 -0
  195. package/useDemoController/instantiateModule.mjs +24 -0
  196. package/useDemoController/resolveCssImports.d.mts +27 -0
  197. package/useDemoController/resolveCssImports.mjs +79 -0
  198. package/useDemoController/transformCode.d.mts +14 -0
  199. package/useDemoController/transformCode.mjs +42 -0
  200. package/useDemoController/transpileClientSingleton.d.mts +10 -0
  201. package/useDemoController/transpileClientSingleton.mjs +83 -0
  202. package/useDemoController/transpileSource.d.mts +29 -0
  203. package/useDemoController/transpileSource.mjs +36 -0
  204. package/useDemoController/transpileWorker.d.mts +14 -0
  205. package/useDemoController/transpileWorker.mjs +41 -0
  206. package/useDemoController/types.d.mts +30 -0
  207. package/useDemoController/types.mjs +1 -0
  208. package/useDemoController/useDemoController.d.mts +54 -0
  209. package/useDemoController/useDemoController.mjs +186 -0
  210. package/useDemoController/useRunner.d.mts +53 -0
  211. package/useDemoController/useRunner.mjs +118 -0
  212. package/useDemoController/useVariantBuilds.d.mts +31 -0
  213. package/useDemoController/useVariantBuilds.mjs +188 -0
  214. package/useSearch/types.d.mts +1 -1
  215. package/useType/TypesDataProvider.d.mts +1 -1
  216. package/useType/TypesDataProvider.mjs +1 -2
  217. package/useType/useType.d.mts +1 -1
  218. package/useType/useTypeProp.d.mts +1 -1
  219. package/withDocsInfra/withDeploymentConfig.mjs +57 -0
  220. package/withDocsInfra/withDocsInfra.d.mts +14 -0
  221. package/withDocsInfra/withDocsInfra.mjs +25 -11
@@ -281,12 +281,16 @@ export const transformMarkdownCode = (options = {}) => {
281
281
  tagName: 'code',
282
282
  data: {
283
283
  hName: 'code',
284
- hProperties: codeHProperties
284
+ hProperties: codeHProperties,
285
+ // Pass the source as raw hast via `hChildren` so remark-rehype
286
+ // skips its mdast `text` handler, which line-trims (and would
287
+ // strip the per-line indentation) on the way to hast.
288
+ hChildren: [{
289
+ type: 'text',
290
+ value: codeNode.value
291
+ }]
285
292
  },
286
- children: [{
287
- type: 'text',
288
- value: codeNode.value
289
- }]
293
+ children: []
290
294
  }]
291
295
  };
292
296
  const outputElement = {
@@ -607,12 +611,15 @@ export const transformMarkdownCode = (options = {}) => {
607
611
  data: {
608
612
  hName: 'code',
609
613
  hProperties: codeHProperties,
610
- meta: `variant=${block.variant}${Object.entries(block.props).map(([key, value]) => ` ${key}=${value}`).join('')}`
614
+ meta: `variant=${block.variant}${Object.entries(block.props).map(([key, value]) => ` ${key}=${value}`).join('')}`,
615
+ // Raw hast `hChildren` to keep per-line indentation;
616
+ // see the filename branch above.
617
+ hChildren: [{
618
+ type: 'text',
619
+ value: block.node.value
620
+ }]
611
621
  },
612
- children: [{
613
- type: 'text',
614
- value: block.node.value
615
- }]
622
+ children: []
616
623
  }]
617
624
  }]
618
625
  }]
@@ -745,12 +752,15 @@ export const transformMarkdownCode = (options = {}) => {
745
752
  data: {
746
753
  hName: 'code',
747
754
  hProperties: codeHProperties,
748
- meta: `variant=${block.variant}${Object.entries(block.props).map(([key, value]) => ` ${key}=${value}`).join('')}`
755
+ meta: `variant=${block.variant}${Object.entries(block.props).map(([key, value]) => ` ${key}=${value}`).join('')}`,
756
+ // Raw hast `hChildren` to keep per-line indentation;
757
+ // see the filename branch above.
758
+ hChildren: [{
759
+ type: 'text',
760
+ value: block.node.value
761
+ }]
749
762
  },
750
- children: [{
751
- type: 'text',
752
- value: block.node.value
753
- }]
763
+ children: []
754
764
  }]
755
765
  }]
756
766
  }]
@@ -834,6 +834,9 @@ export const transformMarkdownMetadata = (options = {}) => {
834
834
  if (options.extractToIndex.indexWrapperComponent) {
835
835
  updateOptions.indexWrapperComponent = options.extractToIndex.indexWrapperComponent;
836
836
  }
837
+ if (options.extractToIndex.cacheDir) {
838
+ updateOptions.cacheDir = options.extractToIndex.cacheDir;
839
+ }
837
840
  }
838
841
  await syncPageIndex(updateOptions);
839
842
  } catch (error) {
@@ -1,5 +1,5 @@
1
1
  import type { PhrasingContent } from 'mdast';
2
- import { type Audience } from "../../createSitemap/types.mjs";
2
+ import type { Audience } from "../../createSitemap/types.mjs";
3
3
  /**
4
4
  * Base options for syncing page indexes.
5
5
  * Shared between extractToIndex (markdown metadata) and updateParentIndex (types).
@@ -28,6 +28,13 @@ export type SyncPageIndexBaseOptions = {
28
28
  * @default false
29
29
  */
30
30
  errorIfOutOfDate?: boolean;
31
+ /**
32
+ * Directory for the sha256-validated JSON cache of page indexes. When set, each
33
+ * index written is also cached at `{cacheDir}/pages-index/{route}.json`, leaving it
34
+ * warm for the next cold `loadServerPageIndex` read.
35
+ * @default undefined (disabled)
36
+ */
37
+ cacheDir?: string;
31
38
  };
32
39
  /**
33
40
  * Plugin options for transformMarkdownMetadata
@@ -1,4 +1,4 @@
1
- import { type Position } from "./useEditableUtils.mjs";
1
+ import type { Position } from "./useEditableUtils.mjs";
2
2
  import type { EditingEngineLoader } from "./editingEngineCache.mjs";
3
3
  type History = [Position, string];
4
4
  export interface State {
package/useCode/Pre.d.mts CHANGED
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import type { SetSource } from "./useSourceEditing.mjs";
3
3
  import type { VariantSource } from "../CodeHighlighter/types.mjs";
4
4
  import type { FallbackNode } from "../CodeHighlighter/fallbackFormat.mjs";
5
- import { type SourceLineCounts } from "./sourceLineCounts.mjs";
5
+ import type { SourceLineCounts } from "./sourceLineCounts.mjs";
6
6
  export declare function Pre({
7
7
  children,
8
8
  className,
@@ -22,7 +22,8 @@ export declare function Pre({
22
22
  onTransitionReady,
23
23
  swapTarget,
24
24
  editActivation,
25
- onActivate
25
+ onActivate,
26
+ editable
26
27
  }: {
27
28
  children: VariantSource;
28
29
  className?: string;
@@ -160,4 +161,10 @@ export declare function Pre({
160
161
  * the live-editing engine, grammars, and worker at the activation moment.
161
162
  */
162
163
  onActivate?: () => void;
164
+ /**
165
+ * Whether edit mode is on. When `false` the block stays read-only — `useEditable` is
166
+ * never enabled, so there's no `contentEditable`, no engine load, and no `onActivate`.
167
+ * Defaults to `true`; the host drives it from the `editable` toggle `useCode` returns.
168
+ */
169
+ editable?: boolean;
163
170
  }): React.ReactNode;
package/useCode/Pre.mjs CHANGED
@@ -240,7 +240,8 @@ export function Pre({
240
240
  onTransitionReady,
241
241
  swapTarget,
242
242
  editActivation,
243
- onActivate
243
+ onActivate,
244
+ editable = true
244
245
  }) {
245
246
  // Defer the decompressing `decodeHastSource` to a post-paint render ONLY when the
246
247
  // first-paint `.fallback` is ALREADY highlighted — i.e. the promoted highlighted-visible
@@ -419,7 +420,7 @@ export function Pre({
419
420
  const collapsedBounds = React.useMemo(() => expanded ? undefined : computeCollapsedBounds(hast, indentation, collapseToEmpty), [hast, expanded, collapseToEmpty]);
420
421
  useEditable(preRef, onEditableChange, {
421
422
  indentation,
422
- disabled: !setSource || !editableReady,
423
+ disabled: !setSource || !editableReady || !editable,
423
424
  minColumn: collapsedBounds?.minColumn,
424
425
  minRow: collapsedBounds?.minRow,
425
426
  maxRow: collapsedBounds?.maxRow,
@@ -874,7 +875,7 @@ export function Pre({
874
875
  // Collapse-to-empty empties the collapsed window, so the focused-line count is 0
875
876
  // regardless of the precomputed value.
876
877
  const sourceFocusedLines = collapseToEmpty ? 0 : rawFocusedLines;
877
- const isEditable = Boolean(setSource);
878
+ const isEditable = Boolean(setSource) && editable;
878
879
 
879
880
  // Focus-trap state for editable code blocks. When the user tabs into the
880
881
  // wrapper (keyboard-only, gated by `:focus-visible`), an overlay prompts
@@ -1,4 +1,4 @@
1
- import { type TransformRuntimeDeps } from "../pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs";
1
+ import type { TransformRuntimeDeps } from "../pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs";
2
2
  import type { VariantSource, VariantCode, Transforms, SourceComments, Fallbacks } from "../CodeHighlighter/types.mjs";
3
3
  import type { FallbackNode } from "../CodeHighlighter/fallbackFormat.mjs";
4
4
  import type { TransformedFiles } from "./useCodeUtils.mjs";
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { ContentProps, SourceEnhancers } from "../CodeHighlighter/types.mjs";
3
- import { type UseCopierOpts } from "../useCopier/index.mjs";
3
+ import type { UseCopierOpts } from "../useCopier/index.mjs";
4
4
  export type UseCodeOpts = {
5
5
  preClassName?: string;
6
6
  copy?: UseCopierOpts;
@@ -203,6 +203,22 @@ export interface UseCodeResult<T extends {} = {}> {
203
203
  * stays anchored to the demo the user interacted with.
204
204
  */
205
205
  pendingTransform: string | null | undefined;
206
+ /**
207
+ * Whether edit mode is currently on. `true` by default; starts `false` when the block
208
+ * opts in via `initialDisabled` — per demo through the `createDemo` `meta`, or across a
209
+ * factory's demos through `createDemoFactory`. While `false` the block renders
210
+ * read-only and the live-editing engine is not even warmed; flip it with
211
+ * {@link setEditable}. Independent of `editActivation`, which governs *when* the engine
212
+ * loads once editing is on, and of `disabled`, which turns editing off permanently.
213
+ */
214
+ editable: boolean;
215
+ /**
216
+ * Turns edit mode on or off. `undefined` when the block can't be edited at all — no
217
+ * `CodeControllerContext` with `setCode` in scope, or editing is hard-`disabled` — so
218
+ * a toggle button can render only when this is defined. Toggling off keeps the
219
+ * reader's edits (use {@link reset} to discard them).
220
+ */
221
+ setEditable?: (editable: boolean) => void;
206
222
  /**
207
223
  * Replace the source of the currently selected file (or `fileName` when
208
224
  * provided) in the controlled code. Internal hooks may pass additional
@@ -149,10 +149,12 @@ export function useCode(contentProps, opts) {
149
149
  // disagree.
150
150
  const collapseToEmpty = contentProps.collapseToEmpty === true;
151
151
  const initialExpanded = contentProps.initialExpanded === true;
152
+ const initialDisabled = contentProps.initialDisabled === true;
152
153
 
153
154
  // Sub-hook: UI State Management (needs slug to check for relevant hash)
154
155
  const uiState = useUIState({
155
156
  initialExpanded,
157
+ initialDisabled,
156
158
  mainSlug: userProps.slug
157
159
  });
158
160
 
@@ -347,6 +349,7 @@ export function useCode(contentProps, opts) {
347
349
  setSource: sourceEditing.setSource,
348
350
  editActivation: context?.editActivation,
349
351
  onActivate: context?.onEditingActivated,
352
+ editable: uiState.editable,
350
353
  effectiveCode,
351
354
  fileHashMode,
352
355
  saveHashVariantToLocalStorage,
@@ -377,6 +380,22 @@ export function useCode(contentProps, opts) {
377
380
  title: userProps.name,
378
381
  copyOpts
379
382
  });
383
+
384
+ // Editing can be toggled only where it's possible at all: a controller with `setCode`
385
+ // is in scope and the block isn't hard-`disabled`. Otherwise `setEditable` is omitted,
386
+ // so a host renders no toggle.
387
+ const canToggleEditing = Boolean(controllerContext?.setCode) && !disabled;
388
+
389
+ // Discard live edits when the reader switches language.
390
+ const rawSelectTransform = transformManagement.selectTransform;
391
+ const resetSource = sourceEditing.reset;
392
+ const hasControlledEdits = Boolean(controllerContext?.code);
393
+ const selectTransform = React.useCallback(transformName => {
394
+ if (hasControlledEdits) {
395
+ resetSource?.();
396
+ }
397
+ rawSelectTransform(transformName);
398
+ }, [hasControlledEdits, resetSource, rawSelectTransform]);
380
399
  return {
381
400
  variants: variantSelection.variantKeys,
382
401
  selectedVariant: variantSelection.selectedVariantKey,
@@ -396,8 +415,10 @@ export function useCode(contentProps, opts) {
396
415
  copyMarkdown: copyFunctionality.copyMarkdown,
397
416
  availableTransforms: transformManagement.availableTransforms,
398
417
  selectedTransform: transformManagement.selectedTransform,
399
- selectTransform: transformManagement.selectTransform,
418
+ selectTransform,
400
419
  pendingTransform: transformManagement.pendingTransform,
420
+ editable: uiState.editable,
421
+ setEditable: canToggleEditing ? uiState.setEditable : undefined,
401
422
  setSource: sourceEditing.setSource,
402
423
  reset: sourceEditing.reset,
403
424
  refresh: context?.refresh,
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
- import { type UseCopierOpts } from "../useCopier/index.mjs";
2
+ import type { UseCopierOpts } from "../useCopier/index.mjs";
3
3
  import type { Fallbacks, VariantCode, VariantSource } from "../CodeHighlighter/types.mjs";
4
4
  import type { FallbackNode } from "../CodeHighlighter/fallbackFormat.mjs";
5
- import { type MarkdownFile } from "./generateVariantMarkdown.mjs";
5
+ import type { MarkdownFile } from "./generateVariantMarkdown.mjs";
6
6
  import type { TransformedFiles } from "./useCodeUtils.mjs";
7
7
  interface UseCopyFunctionalityProps {
8
8
  selectedFile: VariantSource | null;
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, afterEach, beforeAll } from 'vitest';
2
2
  import { renderHook, act } from '@testing-library/react';
3
3
  import { userEvent } from 'vitest/browser';
4
4
  import { useEditable, preloadEditableEngine } from "./useEditable.mjs";
5
-
6
5
  // `useEditable` loads its heavy runtime (the `EditableEngine` chunk) on demand
7
6
  // and only applies `contentEditable` once it resolves. Warm that load once so
8
7
  // the synchronous assertions below see `contentEditable` applied within `act`,
@@ -30,6 +30,8 @@ interface UseFileNavigationProps {
30
30
  editActivation?: 'eager' | 'interaction';
31
31
  /** Forwarded to `<Pre>` / `useEditable`: fired once when the block engages for editing. */
32
32
  onActivate?: () => void;
33
+ /** Forwarded to `<Pre>`: whether edit mode is on. When `false` the block stays read-only. */
34
+ editable?: boolean;
33
35
  effectiveCode?: Code;
34
36
  selectVariant?: React.Dispatch<React.SetStateAction<string>>;
35
37
  fileHashMode?: 'remove-hash' | 'remove-filename';
@@ -153,6 +155,7 @@ export declare function useFileNavigation({
153
155
  setSource,
154
156
  editActivation,
155
157
  onActivate,
158
+ editable,
156
159
  effectiveCode,
157
160
  selectVariant,
158
161
  fileHashMode,
@@ -70,6 +70,7 @@ export function useFileNavigation({
70
70
  setSource,
71
71
  editActivation,
72
72
  onActivate,
73
+ editable,
73
74
  effectiveCode,
74
75
  selectVariant,
75
76
  fileHashMode = 'remove-hash',
@@ -542,6 +543,7 @@ export function useFileNavigation({
542
543
  setSource: setSource,
543
544
  editActivation: editActivation,
544
545
  onActivate: onActivate,
546
+ editable: editable,
545
547
  shouldHighlight: shouldHighlight,
546
548
  fallback: selectedFileFallback,
547
549
  fallbackLineCounts: selectedFileLineCounts,
@@ -555,7 +557,7 @@ export function useFileNavigation({
555
557
  }, getPreRenderKey(fileSlug, enhancementPhase));
556
558
  }
557
559
  return null;
558
- }, [selectedVariant, shouldHighlight, preClassName, setSource, editActivation, onActivate, enhancedSource, isEnhancing, mainSlug, selectedFile, selectedVariantKey, sourceEnhancers, selectedFileNameInternal, selectedFileFallback, selectedFileLineCounts, expanded, collapseToEmpty, expand, transforming, onPreTransitionReady, variantBridgeLineMode, resolveSwapTarget]);
560
+ }, [selectedVariant, shouldHighlight, preClassName, setSource, editActivation, onActivate, editable, enhancedSource, isEnhancing, mainSlug, selectedFile, selectedVariantKey, sourceEnhancers, selectedFileNameInternal, selectedFileFallback, selectedFileLineCounts, expanded, collapseToEmpty, expand, transforming, onPreTransitionReady, variantBridgeLineMode, resolveSwapTarget]);
559
561
  const selectedFileLines = React.useMemo(() => {
560
562
  if (selectedFile == null) {
561
563
  return 0;
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  // (via `useCopyFunctionality`/`Pre`). Passing it into the lazy editing engine
4
4
  // keeps that engine chunk from statically pulling it (and `hastDecompress`).
5
5
  import { stringOrHastToString } from "../pipeline/hastUtils/index.mjs";
6
+ import { preParsedCacheKey } from "../CodeHighlighter/parseControlledCode.mjs";
6
7
  import { useCodeContext } from "../CodeProvider/CodeContext.mjs";
7
8
  import { peekEditingEngine, loadEditingEngine, preloadEditingEngine, resetEditingEngineCache } from "./editingEngineCache.mjs";
8
9
  // The edit-time runtime (`analyzeSource`/`shiftComments`/`toControlledCode`) lives
@@ -50,11 +51,11 @@ export function useSourceEditing({
50
51
  editingEngineLoader
51
52
  } = useCodeContext();
52
53
 
53
- // Monotonic token bumped by every `setSource`/`reset`. A cold first edit
54
- // defers its commit into a microtask; if a later edit or a `reset` happens
55
- // before the engine resolves, the stale deferred commit must NOT run (it would
56
- // re-apply a superseded edit and, after a reset, reverse it). The deferred
57
- // callback captures the token at schedule time and bails if it changed.
54
+ // Monotonic token bumped by every `setSource`/`reset`. A cold first edit defers
55
+ // its commit until the engine chunk resolves; if a later edit or a `reset`
56
+ // happens before then, the stale deferred commit must NOT run (it would re-apply
57
+ // a superseded edit and, after a reset, reverse it). The deferred callback
58
+ // captures the token at schedule time and bails if it changed.
58
59
  const editTokenRef = React.useRef(0);
59
60
  const setSource = React.useCallback((source, fileName, position, preParsed) => {
60
61
  if (!contextSetCode) {
@@ -76,13 +77,13 @@ export function useSourceEditing({
76
77
  const resolvedFileName = fileName ?? selectedVariant?.fileName;
77
78
  const preParsedCache = context?.preParsedCache;
78
79
  if (preParsed !== undefined && preParsedCache && resolvedFileName) {
79
- preParsedCache.set(resolvedFileName, {
80
+ preParsedCache.set(preParsedCacheKey(selectedVariantKey, resolvedFileName), {
80
81
  source,
81
82
  hast: preParsed
82
83
  });
83
84
  }
84
85
  const applyUpdate = engine => {
85
- contextSetCode(currentCode => {
86
+ const applyEdit = currentCode => {
86
87
  const newCode = currentCode ? {
87
88
  ...currentCode
88
89
  } : engine.toControlledCode(effectiveCode, selectedVariantKey, context?.fallbacks, stringOrHastToString);
@@ -175,8 +176,37 @@ export function useSourceEditing({
175
176
  }
176
177
  };
177
178
  }
179
+ const editedVariant = newCode[selectedVariantKey];
180
+ if (editedVariant) {
181
+ if (!currentCode) {
182
+ // FIRST edit (precomputed → controlled): carry the pre-edit build
183
+ // inputs so the runner renders the ORIGINAL as a baseline before this
184
+ // (possibly broken) edit, then swaps to it (see `useVariantBuilds`).
185
+ newCode[selectedVariantKey] = {
186
+ ...editedVariant,
187
+ original: {
188
+ source: variant.source,
189
+ extraFiles: variant.extraFiles
190
+ }
191
+ };
192
+ } else if (editedVariant.original) {
193
+ // Subsequent edit: the baseline already rendered — drop `original` so
194
+ // it doesn't linger in (and bloat) the controlled state.
195
+ const stripped = {
196
+ ...editedVariant
197
+ };
198
+ delete stripped.original;
199
+ newCode[selectedVariantKey] = stripped;
200
+ }
201
+ }
178
202
  return newCode;
179
- });
203
+ };
204
+
205
+ // A SINGLE update carries the edited code to the controller — which the
206
+ // editor reflects immediately (no seed/handoff dance). On the FIRST edit
207
+ // `applyEdit` tags it with `.original`, so the runner renders the original
208
+ // as a baseline before swapping to this edit (see `useVariantBuilds`).
209
+ contextSetCode(applyEdit);
180
210
  };
181
211
 
182
212
  // Apply synchronously from the warm cache (the common case — the warm
@@ -211,8 +241,13 @@ export function useSourceEditing({
211
241
  }
212
242
  // Supersede any pending cold edit so it can't re-apply after this reset.
213
243
  editTokenRef.current += 1;
214
- contextSetCode(undefined);
215
- }, [contextSetCode]);
244
+ // Evict any pre-parsed HAST so the original re-parses fresh on the next render
245
+ // (matches `refresh()`); a stale entry keyed by (variant, fileName) would otherwise
246
+ // survive the reset and the source viewer could reuse it for the rebuilt original.
247
+ context?.preParsedCache?.clear();
248
+ // Back to an empty controlled state, so the next edit re-tags `.original`.
249
+ contextSetCode(null);
250
+ }, [contextSetCode, context?.preParsedCache]);
216
251
  const isEditable = !disabled && Boolean(contextSetCode) && Boolean(selectedVariant);
217
252
  const canReset = !disabled && Boolean(contextSetCode);
218
253
  return {
@@ -4,6 +4,13 @@ import * as React from 'react';
4
4
  import { decodeHastSource } from "../pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs";
5
5
  import { recordEnhancerApplied, shouldSkipEnhancer } from "../pipeline/loadIsomorphicCodeVariant/runSourceEnhancers.mjs";
6
6
 
7
+ // Safety-net deadline (ms) for the async enhancer pass. A client-side enhancer that
8
+ // REJECTS or HANGS would otherwise leave `asyncWork` non-null forever — keeping
9
+ // `isEnhancing` true and locking the consumer (`<Pre>` via `useFileNavigation`) at its
10
+ // un-enhanced 'base' phase until a full page reload. On failure/timeout we settle to the
11
+ // already sync-enhanced result so the phase advances.
12
+ const ENHANCER_TIMEOUT_MS = 10_000;
13
+
7
14
  /**
8
15
  * Resolves a `VariantSource` to a HAST root that is safe to mutate.
9
16
  *
@@ -194,22 +201,47 @@ export function useSourceEnhancing({
194
201
  const enhancers = sourceEnhancers;
195
202
  const name = fileName || 'unknown';
196
203
  let cancelled = false;
197
- async function continueEnhancing() {
198
- const asyncResult = await firstAsyncPromise;
199
- if (cancelled) {
200
- return;
201
- }
202
- const final = await applyEnhancersFrom(asyncResult, comments, name, enhancers, asyncStartIndex + 1);
204
+
205
+ // Clear `asyncWork` (keeping whatever enhanced source we already have) so
206
+ // `isEnhancing` drops and the consumer's phase advances. Used by the failure and
207
+ // timeout paths below — without it a rejected/hung enhancer wedges 'base' forever.
208
+ const settleWithCurrent = () => {
203
209
  if (!cancelled) {
204
- setState({
205
- enhancedSource: final,
210
+ setState(previous => ({
211
+ enhancedSource: previous.enhancedSource,
206
212
  asyncWork: null
207
- });
213
+ }));
214
+ }
215
+ };
216
+
217
+ // Safety net for a hung enhancer (a promise that never settles): force-settle after
218
+ // the deadline so the un-enhanced phase isn't locked until a reload.
219
+ const timer = setTimeout(settleWithCurrent, ENHANCER_TIMEOUT_MS);
220
+ async function continueEnhancing() {
221
+ try {
222
+ const asyncResult = await firstAsyncPromise;
223
+ if (cancelled) {
224
+ return;
225
+ }
226
+ const final = await applyEnhancersFrom(asyncResult, comments, name, enhancers, asyncStartIndex + 1);
227
+ if (!cancelled) {
228
+ setState({
229
+ enhancedSource: final,
230
+ asyncWork: null
231
+ });
232
+ }
233
+ } catch (error) {
234
+ // A rejected async enhancer must not strand `asyncWork` non-null forever.
235
+ console.error('Async source enhancer failed; settling with the current result.', error);
236
+ settleWithCurrent();
237
+ } finally {
238
+ clearTimeout(timer);
208
239
  }
209
240
  }
210
241
  continueEnhancing();
211
242
  return () => {
212
243
  cancelled = true;
244
+ clearTimeout(timer);
213
245
  };
214
246
  }, [state.asyncWork, sourceEnhancers, fileName, comments]);
215
247
  return {
@@ -1,8 +1,8 @@
1
1
  import type { Code, VariantCode } from "../CodeHighlighter/types.mjs";
2
2
  import type { CreateTransformedFiles } from "./TransformEngine.mjs";
3
3
  import { preloadTransformEngine, resetTransformEngineCache } from "./transformEngineCache.mjs";
4
- import { type CodeHighlighterContextType } from "../CodeHighlighter/CodeHighlighterContext.mjs";
5
- import { type TransitionPhase } from "./useTransitionPhase.mjs";
4
+ import type { CodeHighlighterContextType } from "../CodeHighlighter/CodeHighlighterContext.mjs";
5
+ import type { TransitionPhase } from "./useTransitionPhase.mjs";
6
6
  export { preloadTransformEngine, resetTransformEngineCache };
7
7
  interface UseTransformManagementProps {
8
8
  context?: CodeHighlighterContextType;
@@ -13,7 +13,6 @@ import { usePreference } from "../usePreference/index.mjs";
13
13
  import { useCoordinated } from "../useCoordinated/index.mjs";
14
14
  import { useHighlightGate } from "./useHighlightGate.mjs";
15
15
  import { useTransitionPhase } from "./useTransitionPhase.mjs";
16
-
17
16
  // Stable identity for the hast helpers handed to the transform engine; both are
18
17
  // module-level functions, so this never needs to change.
19
18
  const transformRuntimeDeps = {
@@ -123,7 +122,19 @@ export function useTransformManagement({
123
122
  // here, otherwise rename-only entries would be dropped from
124
123
  // resolution and the storage key would shift whenever a transform's
125
124
  // visibility changed between sibling demos.
126
- const applicableTransforms = React.useMemo(() => getApplicableTransforms(effectiveCode, selectedVariantKey), [effectiveCode, selectedVariantKey]);
125
+ const contextAvailableTransforms = context?.availableTransforms;
126
+ // What the CURRENT (possibly edited) code can actually apply. Empty while
127
+ // live-editing, since controlled code carries no transform manifest.
128
+ const applicableFromCode = React.useMemo(() => getApplicableTransforms(effectiveCode, selectedVariantKey), [effectiveCode, selectedVariantKey]);
129
+ const applicableTransforms = React.useMemo(() => {
130
+ // Fall back to the controller's visible list while editing so a language
131
+ // switch still validates — it discards the edit and re-applies to the
132
+ // pristine build-time source (see `useCode.selectTransform`).
133
+ if (applicableFromCode.length === 0 && contextAvailableTransforms?.length) {
134
+ return contextAvailableTransforms;
135
+ }
136
+ return applicableFromCode;
137
+ }, [applicableFromCode, contextAvailableTransforms]);
127
138
 
128
139
  // Coordinator key. Demos sharing the same applicable transform set
129
140
  // belong to the same coordination group: a user click in one demo
@@ -510,6 +521,11 @@ export function useTransformManagement({
510
521
  // `onCommit` when its `(variant, transform)` keys match the values
511
522
  // about to be rendered.
512
523
  const transformedFiles = React.useMemo(() => {
524
+ // While live-editing, `selectedTransform` may still be e.g. `js`, so
525
+ // return `undefined` to render the edited source as-is (see `useFileNavigation`).
526
+ if (delayedAppliedTransform && !applicableFromCode.includes(delayedAppliedTransform)) {
527
+ return undefined;
528
+ }
513
529
  if (precomputed && precomputed.variant === selectedVariant && precomputed.transform === delayedAppliedTransform) {
514
530
  return precomputed.result;
515
531
  }
@@ -522,7 +538,7 @@ export function useTransformManagement({
522
538
  return undefined;
523
539
  }
524
540
  return transformEngine(selectedVariant, delayedAppliedTransform, transformRuntimeDeps, context?.fallbacks);
525
- }, [precomputed, selectedVariant, delayedAppliedTransform, context?.fallbacks, transformEngine]);
541
+ }, [precomputed, selectedVariant, delayedAppliedTransform, applicableFromCode, context?.fallbacks, transformEngine]);
526
542
  const result = {
527
543
  availableTransforms,
528
544
  selectedTransform,
@@ -1,12 +1,15 @@
1
1
  import * as React from 'react';
2
2
  interface UseUIStateProps {
3
3
  initialExpanded?: boolean;
4
+ initialDisabled?: boolean;
4
5
  mainSlug?: string;
5
6
  }
6
7
  export interface UseUIStateResult {
7
8
  expanded: boolean;
8
9
  expand: () => void;
9
10
  setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
11
+ editable: boolean;
12
+ setEditable: React.Dispatch<React.SetStateAction<boolean>>;
10
13
  }
11
14
  /**
12
15
  * Hook for managing UI state like expansion and focus
@@ -14,6 +17,7 @@ export interface UseUIStateResult {
14
17
  */
15
18
  export declare function useUIState({
16
19
  initialExpanded,
20
+ initialDisabled,
17
21
  mainSlug
18
22
  }: UseUIStateProps): UseUIStateResult;
19
23
  export {};
@@ -7,6 +7,7 @@ import { isHashRelevantToDemo } from "./useFileNavigation.mjs";
7
7
  */
8
8
  export function useUIState({
9
9
  initialExpanded = false,
10
+ initialDisabled = false,
10
11
  mainSlug
11
12
  }) {
12
13
  const [hash] = useUrlHashState();
@@ -14,6 +15,10 @@ export function useUIState({
14
15
  const [expanded, setExpanded] = React.useState(initialExpanded || hasRelevantHash);
15
16
  const expand = React.useCallback(() => setExpanded(true), []);
16
17
 
18
+ // Edit-mode toggle. Starts on unless the block opted into `initialDisabled`; the host
19
+ // exposes `setEditable` (gated on a controller) so a reader can flip read-only ↔ edit.
20
+ const [editable, setEditable] = React.useState(!initialDisabled);
21
+
17
22
  // Auto-expand if hash becomes relevant. This is a one-way OR-latch: it ratchets
18
23
  // `expanded` to true but never collapses, so adjusting state during render is safe
19
24
  // (the branch is skipped once `expanded` is true, avoiding an extra render).
@@ -23,6 +28,8 @@ export function useUIState({
23
28
  return {
24
29
  expanded,
25
30
  expand,
26
- setExpanded
31
+ setExpanded,
32
+ editable,
33
+ setEditable
27
34
  };
28
35
  }
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { Code, VariantCode } from "../CodeHighlighter/types.mjs";
3
- import { type TransitionPhase } from "./useTransitionPhase.mjs";
3
+ import type { TransitionPhase } from "./useTransitionPhase.mjs";
4
4
  interface UseVariantSelectionProps {
5
5
  effectiveCode: Code;
6
6
  initialVariant?: string;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type ChannelKey, type PeerId } from "./coordinatePreference.mjs";
2
+ import type { ChannelKey, PeerId } from "./coordinatePreference.mjs";
3
3
  /**
4
4
  * Options for {@link useCoordinated}. See `coordinatePreference` for
5
5
  * the underlying semantics; only React-specific behaviors are
@@ -1,5 +1,5 @@
1
1
  import type * as React from 'react';
2
- import { type UseCoordinatedExtras, type UseCoordinatedOptions } from "./useCoordinated.mjs";
2
+ import type { UseCoordinatedExtras, UseCoordinatedOptions } from "./useCoordinated.mjs";
3
3
  /**
4
4
  * `useLocalStorageState` + coordination in one call. Cross-tab sync
5
5
  * happens via the underlying storage events; the coordinator handles
@@ -2,7 +2,6 @@
2
2
 
3
3
  import useLocalStorageState from "../useLocalStorageState/index.mjs";
4
4
  import { useCoordinated } from "./useCoordinated.mjs";
5
-
6
5
  /**
7
6
  * `useLocalStorageState` + coordination in one call. Cross-tab sync
8
7
  * happens via the underlying storage events; the coordinator handles
@@ -1,5 +1,5 @@
1
1
  import type * as React from 'react';
2
- import { type UseCoordinatedExtras, type UseCoordinatedOptions } from "./useCoordinated.mjs";
2
+ import type { UseCoordinatedExtras, UseCoordinatedOptions } from "./useCoordinated.mjs";
3
3
  /**
4
4
  * `usePreference` + {@link useCoordinated} in one call.
5
5
  *