@riverbankcms/sdk 0.79.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/dist/PreviewEditorSidebar-RDCMEXIJ.mjs +3163 -0
  2. package/dist/{PreviewEditorUI-27KJVEET.mjs → PreviewEditorUI-NYGQ6K7B.mjs} +194 -30
  3. package/dist/SdkPreviewModeRuntime-AKCSCZEC.mjs +8 -0
  4. package/dist/_dts/ai/src/contracts/proposals.d.ts +20 -0
  5. package/dist/_dts/api/src/contentRuntime.d.ts +12 -0
  6. package/dist/_dts/api/src/contentTypes.d.ts +6 -0
  7. package/dist/_dts/api/src/endpoints.d.ts +24 -0
  8. package/dist/_dts/api/src/index.d.ts +3 -3
  9. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +53 -0
  10. package/dist/_dts/api/src/siteInfrastructure.d.ts +124 -0
  11. package/dist/_dts/api/src/siteOnboarding.d.ts +13 -0
  12. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +10 -1
  13. package/dist/_dts/block-form/src/backend/store-backend.d.ts +3 -1
  14. package/dist/_dts/block-form/src/backend/types.d.ts +3 -3
  15. package/dist/_dts/block-form/src/client/index.d.ts +2 -0
  16. package/dist/_dts/block-form/src/index.d.ts +2 -0
  17. package/dist/_dts/block-form/src/utils/evaluateVisibility.d.ts +2 -1
  18. package/dist/_dts/blocks/src/PageRenderer.d.ts +3 -0
  19. package/dist/_dts/blocks/src/client/header.d.ts +1 -1
  20. package/dist/_dts/blocks/src/client.d.ts +12 -12
  21. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +20 -28
  22. package/dist/_dts/blocks/src/index.d.ts +7 -5
  23. package/dist/_dts/blocks/src/primitives/content/components/Text.d.ts +1 -1
  24. package/dist/_dts/blocks/src/primitives/content/primitives.d.ts +1 -0
  25. package/dist/_dts/blocks/src/server.d.ts +1 -1
  26. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +3 -2
  27. package/dist/_dts/blocks/src/system/blocks/events/event-listing.d.ts +2 -2
  28. package/dist/_dts/blocks/src/system/blocks/events/event-spotlight.d.ts +2 -1
  29. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +135 -0
  30. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +9 -1
  31. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +2 -0
  32. package/dist/_dts/blocks/src/system/blocks/form.d.ts +20 -1
  33. package/dist/_dts/blocks/src/system/blocks/hero.d.ts +2 -0
  34. package/dist/_dts/blocks/src/system/blocks/offerings-preview/index.d.ts +19 -0
  35. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +134 -0
  36. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +7 -1
  37. package/dist/_dts/blocks/src/system/componentFactory.d.ts +1 -0
  38. package/dist/_dts/blocks/src/system/constants/background.d.ts +1 -1
  39. package/dist/_dts/blocks/src/system/constants/blockKinds.d.ts +1 -1
  40. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +34 -15
  41. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -0
  42. package/dist/_dts/blocks/src/system/data/linkedContentEntries.d.ts +64 -0
  43. package/dist/_dts/blocks/src/system/data/loaderEndpoints.d.ts +1 -1
  44. package/dist/_dts/blocks/src/system/designCapabilities.d.ts +2 -2
  45. package/dist/_dts/blocks/src/system/fields/boxStyles.d.ts +10 -3
  46. package/dist/_dts/blocks/src/system/fragments/library/testimonialsCarousel.d.ts +5 -0
  47. package/dist/_dts/blocks/src/system/media/slotContracts.d.ts +2 -0
  48. package/dist/_dts/blocks/src/system/node/fragments/headingDecoration.d.ts +1 -4
  49. package/dist/_dts/blocks/src/system/node/types.d.ts +8 -1
  50. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +2 -2
  51. package/dist/_dts/blocks/src/system/registry.d.ts +1 -0
  52. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +51 -57
  53. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -27
  54. package/dist/_dts/blocks/src/system/runtime/header/enhanceHeaderSection.d.ts +10 -2
  55. package/dist/_dts/blocks/src/system/runtime/headingDecoration.d.ts +21 -0
  56. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +45 -14
  57. package/dist/_dts/blocks/src/system/runtime/hooks/useEventFilters.d.ts +2 -9
  58. package/dist/_dts/blocks/src/system/runtime/hooks/useEventPagination.d.ts +1 -10
  59. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +1 -0
  60. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +21 -16
  61. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +3 -2
  62. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +21 -16
  63. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventListing.interactive.d.ts +22 -33
  64. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventListing.server.d.ts +2 -2
  65. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventSpotlight.d.ts +2 -2
  66. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventCard.d.ts +2 -2
  67. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventDetailModal.d.ts +2 -2
  68. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModals.d.ts +2 -2
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventPaginatedListView.client.d.ts +2 -3
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +3 -3
  71. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listOptions.d.ts +50 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +2 -1
  73. package/dist/_dts/blocks/src/system/runtime/nodes/header-section.client.d.ts +2 -2
  74. package/dist/_dts/blocks/src/system/runtime/nodes/media.d.ts +29 -14
  75. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview.d.ts +21 -0
  76. package/dist/_dts/blocks/src/system/runtime/resolver.d.ts +7 -0
  77. package/dist/_dts/blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.d.ts +2 -0
  78. package/dist/_dts/blocks/src/system/transforms/registry/boxStyles.d.ts +7 -2
  79. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +9 -0
  80. package/dist/_dts/blocks/src/system/transforms/typed.d.ts +14 -1
  81. package/dist/_dts/calendar/src/grid.d.ts +27 -0
  82. package/dist/_dts/calendar/src/index.d.ts +2 -2
  83. package/dist/_dts/content-editor/src/controller/useAutosaveTriggers.d.ts +3 -1
  84. package/dist/_dts/content-editor/src/editorTransactionState.d.ts +1 -0
  85. package/dist/_dts/content-editor/src/hooks/autosaveScopePlanning.d.ts +26 -0
  86. package/dist/_dts/content-editor/src/hooks/publishability.d.ts +1 -0
  87. package/dist/_dts/content-editor/src/hooks/useLinkedEntryAutosave.d.ts +23 -0
  88. package/dist/_dts/content-editor/src/hooks/useReactiveToolbar.d.ts +1 -1
  89. package/dist/_dts/content-editor/src/hooks/useSimpleAutosave.d.ts +2 -2
  90. package/dist/_dts/content-editor/src/index.d.ts +3 -1
  91. package/dist/_dts/content-editor/src/lib/json.d.ts +2 -0
  92. package/dist/_dts/content-editor/src/linkedEntries/domain.d.ts +8 -0
  93. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +58 -0
  94. package/dist/_dts/content-editor/src/ui/editorTransactionState.d.ts +93 -0
  95. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +60 -3
  96. package/dist/_dts/content-editor/src/ui/stores/autosaveStore.d.ts +2 -0
  97. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +1 -1
  98. package/dist/_dts/content-model/src/content-types/builtins.d.ts +43 -0
  99. package/dist/_dts/content-model/src/metadataConfig.d.ts +87 -47
  100. package/dist/_dts/core/src/branded.d.ts +5 -0
  101. package/dist/_dts/core/src/index.d.ts +1 -0
  102. package/dist/_dts/core/src/stable-json.d.ts +1 -0
  103. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +9 -3
  104. package/dist/_dts/editor/src/EditorProvider.d.ts +26 -0
  105. package/dist/_dts/editor/src/chrome/EditorChrome.d.ts +26 -0
  106. package/dist/_dts/editor/src/chrome/EditorStatusBar.d.ts +45 -0
  107. package/dist/_dts/editor/src/chrome/statusBarPresentation.d.ts +25 -0
  108. package/dist/_dts/editor/src/components/EditingToggle.d.ts +9 -0
  109. package/dist/_dts/editor/src/hooks/useEditableElement.d.ts +18 -0
  110. package/dist/_dts/editor/src/index.d.ts +29 -0
  111. package/dist/_dts/editor/src/inline/InlineTextEditor.d.ts +1 -0
  112. package/dist/_dts/editor/src/lib/utils.d.ts +1 -0
  113. package/dist/_dts/editor/src/primitives/EditorPrimitivesProvider.d.ts +20 -0
  114. package/dist/_dts/editor/src/primitives/InlineFieldContext.d.ts +11 -0
  115. package/dist/_dts/editor/src/primitives/SaveContext.d.ts +12 -0
  116. package/dist/_dts/editor/src/primitives/helpers.d.ts +1 -0
  117. package/dist/_dts/editor/src/primitives/rich-text/RichTextEditorSurface.d.ts +26 -0
  118. package/dist/_dts/editor/src/primitives/rich-text/RichTextToolbar.d.ts +16 -0
  119. package/dist/_dts/editor/src/primitives/rich-text/config.d.ts +23 -0
  120. package/dist/_dts/editor/src/primitives/rich-text/extensions/RichTextImage.d.ts +2 -0
  121. package/dist/_dts/editor/src/primitives/rich-text/extensions/RichTextLink.d.ts +1 -0
  122. package/dist/_dts/editor/src/primitives/rich-text/imageActions.d.ts +22 -0
  123. package/dist/_dts/editor/src/primitives/rich-text/sanitize.d.ts +23 -0
  124. package/dist/_dts/editor/src/primitives/rich-text/toolbar/BlockTypeDropdown.d.ts +6 -0
  125. package/dist/_dts/editor/src/primitives/rich-text/toolbar/LinkPopover.d.ts +10 -0
  126. package/dist/_dts/editor/src/primitives/rich-text/toolbar/ToolbarIconButton.d.ts +11 -0
  127. package/dist/_dts/editor/src/primitives/rich-text/toolbar/linkPopoverUtils.d.ts +58 -0
  128. package/dist/_dts/editor/src/primitives/rich-text/useRichTextEditor.d.ts +19 -0
  129. package/dist/_dts/editor/src/primitives/rich-text/utils.d.ts +20 -0
  130. package/dist/_dts/editor/src/utils/optimisticTokens.d.ts +27 -0
  131. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +2 -2
  132. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +5 -9
  133. package/dist/_dts/editor-blocks/src/block-editor/BlockCardBody.d.ts +2 -6
  134. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +6 -10
  135. package/dist/_dts/editor-blocks/src/block-editor/FeatureCardIconsPanel.d.ts +2 -6
  136. package/dist/_dts/editor-blocks/src/block-editor/GeneratedIconSetDialog.d.ts +6 -4
  137. package/dist/_dts/editor-blocks/src/block-editor/SortableBlockItem.d.ts +4 -8
  138. package/dist/_dts/editor-blocks/src/block-editor/actionPresentation.d.ts +15 -0
  139. package/dist/_dts/editor-blocks/src/block-editor/blockHeaderActionPlan.d.ts +49 -0
  140. package/dist/_dts/editor-blocks/src/index.d.ts +2 -0
  141. package/dist/_dts/editor-blocks/src/widgets/index.d.ts +1 -0
  142. package/dist/_dts/editor-blocks/src/widgets/rich-text/RichTextWidgetProvider.d.ts +1 -1
  143. package/dist/_dts/editor-blocks/src/widgets/rich-text/useImagePicker.d.ts +1 -1
  144. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/SiteOptionPickerField.d.ts +3 -0
  145. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/SiteOptionWidgetProvider.d.ts +14 -0
  146. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/index.d.ts +6 -0
  147. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/loaders.d.ts +51 -0
  148. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/types.d.ts +20 -0
  149. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/utils.d.ts +5 -0
  150. package/dist/_dts/media-core/src/index.d.ts +1 -0
  151. package/dist/_dts/media-core/src/renderableMediaAsset.d.ts +18 -0
  152. package/dist/_dts/media-editor/src/MediaEditor.d.ts +4 -1
  153. package/dist/_dts/media-editor/src/MediaPickerDialog.d.ts +2 -1
  154. package/dist/_dts/media-editor/src/index.d.ts +1 -1
  155. package/dist/_dts/preview-next/src/client/blocks/BlockToolbar.d.ts +7 -2
  156. package/dist/_dts/preview-next/src/client/blocks/EditablePageRenderer.d.ts +5 -1
  157. package/dist/_dts/preview-next/src/client/blocks/blockCustomizePopoverRows.d.ts +25 -0
  158. package/dist/_dts/preview-next/src/client/blocks/composer/mergeDrafts.d.ts +2 -1
  159. package/dist/_dts/preview-next/src/client/blocks/composer/types.d.ts +4 -30
  160. package/dist/_dts/preview-next/src/client/blocks/linkedEntryRenderOverlay.d.ts +11 -0
  161. package/dist/_dts/preview-next/src/client/blocks/previewCustomizeActions.d.ts +3 -9
  162. package/dist/_dts/preview-next/src/client/blocks/previewCustomizeControlPlan.d.ts +24 -0
  163. package/dist/_dts/preview-next/src/client/blocks/useBlockCustomizeControls.d.ts +35 -0
  164. package/dist/_dts/preview-next/src/client/index.d.ts +0 -1
  165. package/dist/_dts/preview-next/src/client/preview/EntryEditorPanel.d.ts +3 -1
  166. package/dist/_dts/preview-next/src/client/preview/LinkedEntryEditEventBridge.d.ts +1 -0
  167. package/dist/_dts/preview-next/src/client/preview/PageBlockEditorPanel.d.ts +5 -6
  168. package/dist/_dts/preview-next/src/client/preview/PageDesignEditorStateContext.d.ts +4 -1
  169. package/dist/_dts/preview-next/src/client/preview/PreviewBlockCustomizeSelectionContext.d.ts +20 -0
  170. package/dist/_dts/preview-next/src/client/preview/PreviewChromeLayer.d.ts +20 -0
  171. package/dist/_dts/preview-next/src/client/preview/PreviewDesignHistoryOrderContext.d.ts +13 -0
  172. package/dist/_dts/preview-next/src/client/preview/PreviewOpenEditorButton.d.ts +2 -0
  173. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +19 -19
  174. package/dist/_dts/preview-next/src/client/preview/PreviewSidebarSection.d.ts +13 -0
  175. package/dist/_dts/preview-next/src/client/preview/SelectedBlockCustomizePanel.d.ts +13 -0
  176. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +47 -4
  177. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.state.d.ts +14 -1
  178. package/dist/_dts/preview-next/src/client/preview/linkedEntryEditEvents.d.ts +5 -0
  179. package/dist/_dts/preview-next/src/client/preview/previewChromePlacement.d.ts +46 -0
  180. package/dist/_dts/preview-next/src/client/preview/previewCustomizeControls.d.ts +68 -0
  181. package/dist/_dts/preview-next/src/client/preview/previewCustomizeEditorDraftPatch.d.ts +13 -0
  182. package/dist/_dts/preview-next/src/client/preview/previewCustomizeHistory.d.ts +8 -1
  183. package/dist/_dts/preview-next/src/client/preview/previewCustomizeRuntimePatch.d.ts +2 -0
  184. package/dist/_dts/preview-next/src/client/preview/previewCustomizeWorkflowState.d.ts +11 -9
  185. package/dist/_dts/preview-next/src/client/preview/previewDesignHistoryOrder.d.ts +18 -0
  186. package/dist/_dts/preview-next/src/client/preview/previewUndoDomains.d.ts +1 -1
  187. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +41 -0
  188. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +101 -0
  189. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +29 -0
  190. package/dist/_dts/preview-next/src/client/preview/siteStyleHistoryState.d.ts +55 -0
  191. package/dist/_dts/preview-next/src/client/preview/storeTypes.d.ts +4 -0
  192. package/dist/_dts/preview-next/src/client/preview/styleConfiguratorApplyPayload.d.ts +1 -0
  193. package/dist/_dts/preview-next/src/client/widgets/widgetAdapters.d.ts +2 -1
  194. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +6 -3
  195. package/dist/_dts/preview-next/src/constants.d.ts +0 -1
  196. package/dist/_dts/sdk/src/cli/commands/webhooks.d.ts +44 -0
  197. package/dist/_dts/sdk/src/cli/config-loader.d.ts +3 -1
  198. package/dist/_dts/sdk/src/cli/content/reader.d.ts +5 -3
  199. package/dist/_dts/sdk/src/cli/site-commands/commandSurfaceDispatch.d.ts +1 -0
  200. package/dist/_dts/sdk/src/cli/site-commands/pageCommands.d.ts +3 -1
  201. package/dist/_dts/sdk/src/cli/site-commands/pushExecution.d.ts +1 -0
  202. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  203. package/dist/_dts/sdk/src/client/management/types.d.ts +56 -0
  204. package/dist/_dts/sdk/src/client/management/webhooks.d.ts +3 -0
  205. package/dist/_dts/sdk/src/config/types.d.ts +1 -1
  206. package/dist/_dts/sdk/src/config/validation.d.ts +3 -3
  207. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +34 -5
  208. package/dist/_dts/sdk/src/public-api/contracts.d.ts +10 -0
  209. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +42 -0
  210. package/dist/_dts/sdk/src/rendering/mediaAssets.d.ts +4 -0
  211. package/dist/_dts/sdk/src/types/block-kinds.d.ts +1 -1
  212. package/dist/_dts/sdk/src/version.d.ts +1 -1
  213. package/dist/_dts/theme-core/src/accordions/siteStyleDefaults.d.ts +220 -0
  214. package/dist/_dts/theme-core/src/accordions/types.d.ts +45 -2
  215. package/dist/_dts/theme-core/src/blockOverrides.d.ts +8 -0
  216. package/dist/_dts/theme-core/src/blocks/types.d.ts +1 -1
  217. package/dist/_dts/theme-core/src/blocks/vars/testimonials.d.ts +4 -1
  218. package/dist/_dts/theme-core/src/buttons/constants.d.ts +26 -1
  219. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +11 -0
  220. package/dist/_dts/theme-core/src/buttons/personalities/joy-stamp.d.ts +22 -0
  221. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +2 -1
  222. package/dist/_dts/theme-core/src/buttons/types.d.ts +9 -0
  223. package/dist/_dts/theme-core/src/cards/core.d.ts +20 -0
  224. package/dist/_dts/theme-core/src/cards/generateDefaultCardSystem.d.ts +6 -3
  225. package/dist/_dts/theme-core/src/cards/siteStyleDefaults.d.ts +149 -0
  226. package/dist/_dts/theme-core/src/cards/types.d.ts +177 -0
  227. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  228. package/dist/_dts/theme-core/src/interactive/types.d.ts +10 -0
  229. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +180 -21
  230. package/dist/_dts/theme-core/src/palette/variants/index.d.ts +30 -0
  231. package/dist/_dts/theme-core/src/palette/variants/soft-natural.d.ts +16 -0
  232. package/dist/_dts/theme-core/src/palette/variants/types.d.ts +1 -1
  233. package/dist/_dts/theme-core/src/palette/variants/warm-dark.d.ts +27 -0
  234. package/dist/_dts/theme-core/src/schema.d.ts +486 -55
  235. package/dist/_dts/theme-core/src/site-styles/accessibleSurfaces.d.ts +4 -1
  236. package/dist/_dts/theme-core/src/site-styles/appearancePresetCatalog.d.ts +172 -47
  237. package/dist/_dts/theme-core/src/site-styles/appearancePresetResolution.d.ts +6 -2
  238. package/dist/_dts/theme-core/src/site-styles/appearancePresetTypes.d.ts +2 -3
  239. package/dist/_dts/theme-core/src/site-styles/blockAppearancePresets.d.ts +172 -47
  240. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +140 -2
  241. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +18 -2
  242. package/dist/_dts/theme-core/src/site-styles/footerLooks.d.ts +20 -1
  243. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +10 -2
  244. package/dist/_dts/theme-core/src/site-styles/headerLooks.d.ts +291 -6
  245. package/dist/_dts/theme-core/src/site-styles/index.d.ts +3 -0
  246. package/dist/_dts/theme-core/src/site-styles/logoBranding.d.ts +193 -0
  247. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +3 -23
  248. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +276 -4
  249. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +2 -8
  250. package/dist/_dts/theme-core/src/site-styles/rawStyleFields.d.ts +2 -0
  251. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +43 -10
  252. package/dist/_dts/theme-core/src/site-styles/sectionTransitions.d.ts +508 -0
  253. package/dist/_dts/theme-core/src/site-styles/siteStyleThemeCompiler.d.ts +11 -0
  254. package/dist/_dts/theme-core/src/site-styles/sourceCatalogs.d.ts +10 -10
  255. package/dist/_dts/theme-core/src/site-styles/styleTags.d.ts +1 -1
  256. package/dist/_dts/theme-core/src/site-styles/testimonialQuotePresets.d.ts +15 -0
  257. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  258. package/dist/_dts/theme-core/src/site-styles/types.d.ts +28 -32
  259. package/dist/_dts/theme-core/src/site-styles/typographyPresets.d.ts +16 -1
  260. package/dist/{chunk-4HCW7FHJ.mjs → chunk-3PTVPAT4.mjs} +1984 -1142
  261. package/dist/{PreviewEditorSidebar-56NMJMIK.mjs → chunk-3QWQ2MUQ.mjs} +1050 -2609
  262. package/dist/{chunk-GP555I66.mjs → chunk-3VT7LZY2.mjs} +413 -157
  263. package/dist/{chunk-A3HHFKVT.mjs → chunk-4T5JIPWY.mjs} +1 -1
  264. package/dist/{chunk-K2IIF34C.mjs → chunk-E3NZYEKF.mjs} +25 -7
  265. package/dist/{chunk-V47ZU33I.mjs → chunk-WUHKQBB6.mjs} +12062 -3211
  266. package/dist/cli/index.mjs +3395 -817
  267. package/dist/cli/init-docs/content/workflow-isr-revalidation.md +16 -4
  268. package/dist/client/{accordion-QLA7KBYO.mjs → accordion-SFIIZWNH.mjs} +2 -2
  269. package/dist/client/{bookingForm-PYZIPA5G.mjs → bookingForm-CFKZVWGA.mjs} +5 -5
  270. package/dist/client/bookings.mjs +1422 -345
  271. package/dist/client/{carousel-5EIULWBI.mjs → carousel-ELXWSBLA.mjs} +3 -3
  272. package/dist/client/{cart-DOP3FNI4.mjs → cart-6SSCPYY5.mjs} +6 -6
  273. package/dist/client/{checkout-BVMF6FBV.mjs → checkout-UFQ34HPV.mjs} +6 -6
  274. package/dist/client/{chunk-44K2U3QI.mjs → chunk-76JUETXQ.mjs} +139 -186
  275. package/dist/client/{chunk-AUJ5DBZJ.mjs → chunk-7BXX7WXH.mjs} +112 -28
  276. package/dist/client/{chunk-UDS3NTWA.mjs → chunk-G56BSZAM.mjs} +2 -2
  277. package/dist/client/{chunk-V6M5FCR5.mjs → chunk-IUEDYLFJ.mjs} +1 -1
  278. package/dist/client/{chunk-YLDYVRZL.mjs → chunk-NP47RHIX.mjs} +5 -5
  279. package/dist/client/{chunk-R5ESWNKX.mjs → chunk-NX6X3GRF.mjs} +419 -85
  280. package/dist/client/{chunk-CCNTDRCF.mjs → chunk-PA2LJH2M.mjs} +2 -2
  281. package/dist/client/{chunk-CE7IBGVW.mjs → chunk-RMWMT3ZD.mjs} +1 -1
  282. package/dist/client/{chunk-TYONZMST.mjs → chunk-TCJS3NY5.mjs} +1 -1
  283. package/dist/client/{chunk-GG4M2NP6.mjs → chunk-V6XI4M2Q.mjs} +1 -1
  284. package/dist/client/client.mjs +9077 -4410
  285. package/dist/client/{courseRegistration-E6EAA6NL.mjs → courseRegistration-HJIISAAG.mjs} +5 -5
  286. package/dist/client/{eventCalendar-HBG3HAK6.mjs → eventCalendar-F6YVUCZR.mjs} +7 -7
  287. package/dist/client/{eventCombined-6K5ZZWAK.mjs → eventCombined-B5DSTIOK.mjs} +7 -7
  288. package/dist/client/{eventDetails-Q75ICXLT.mjs → eventDetails-SEJDDHQC.mjs} +7 -7
  289. package/dist/client/{eventListing-27P5F7DZ.mjs → eventListing-TPMWIIBO.mjs} +7 -7
  290. package/dist/client/{eventRegistration-IQ7AMASR.mjs → eventRegistration-QU6W2PV6.mjs} +7 -7
  291. package/dist/client/{form-JRTD65FN.mjs → form-LVTYNTDE.mjs} +2 -2
  292. package/dist/client/{headerSection-7FPSSZHS.mjs → headerSection-SO3MFC4U.mjs} +110 -28
  293. package/dist/client/hooks.mjs +4695 -2260
  294. package/dist/client/{locationMap-7NGFNYBH.mjs → locationMap-5E5FLAKO.mjs} +2 -2
  295. package/dist/client/{newsletterForm-F3BADCI2.mjs → newsletterForm-GN32JEBQ.mjs} +2 -2
  296. package/dist/client/{productDetail-V3HP7ITJ.mjs → productDetail-Q346KSVR.mjs} +6 -6
  297. package/dist/client/{productList-L3EGJUF2.mjs → productList-DTDMQYGZ.mjs} +6 -6
  298. package/dist/client/rendering/client.mjs +5795 -3582
  299. package/dist/client/rendering/islands.mjs +32 -20
  300. package/dist/client/rendering.mjs +7988 -3317
  301. package/dist/client/{shop-DQUB5E2C.mjs → shop-OFUW7KPN.mjs} +6 -6
  302. package/dist/preview-next/before-render.mjs +24 -0
  303. package/dist/preview-next/client/runtime.mjs +5 -5
  304. package/dist/preview-next/constants.mjs +0 -2
  305. package/dist/preview-next/index.mjs +0 -2
  306. package/dist/preview-next/middleware.mjs +24 -0
  307. package/dist/{sdk-runtime-AF6KXJU4.mjs → sdk-runtime-2GIX6I7X.mjs} +6 -6
  308. package/dist/server/components.mjs +7384 -3089
  309. package/dist/server/config-validation.mjs +4747 -2418
  310. package/dist/server/config.mjs +4747 -2418
  311. package/dist/server/data.mjs +4695 -2260
  312. package/dist/server/index.mjs +26 -1
  313. package/dist/server/next.mjs +7430 -3039
  314. package/dist/server/page-converter.mjs +3985 -2070
  315. package/dist/server/prebuild.mjs +1 -1
  316. package/dist/server/rendering/server.mjs +7489 -3094
  317. package/dist/server/rendering.mjs +7491 -3096
  318. package/dist/server/routing.mjs +4965 -2386
  319. package/dist/server/server.mjs +4713 -2278
  320. package/dist/server/theme-bridge.mjs +2181 -383
  321. package/dist/server/theme.mjs +477 -222
  322. package/dist/styles/index.css +747 -71
  323. package/package.json +1 -1
  324. package/dist/SdkPreviewModeRuntime-H4BSP6E7.mjs +0 -8
  325. package/dist/_dts/editor-blocks/src/block-editor/actionControlMode.d.ts +0 -2
  326. package/dist/_dts/preview-next/src/client/preview/previewSurface.d.ts +0 -3
  327. package/dist/chunk-LCY32PXF.mjs +0 -1175
@@ -1,11 +1,12 @@
1
1
  "use client";
2
- import { useOptionalPreviewLayout, PreviewQueryProvider } from './chunk-A3HHFKVT.mjs';
3
- import { PreviewThemeOverlayProvider, PreviewDesignCoordinatorProvider, resolveSiteStyleAccess, usePreviewDesignCoordinator, createOptimisticActionRunner, resolveStyleConfiguratorInitialSnapshot, createStyleConfiguratorState, syncStyleConfiguratorState, buildSiteChromeLookModels, planSiteStylePreviewChange, markApplyFailed, markApplySaved, markApplySaving, previewHeaderStyle, previewHeaderLayout, previewFooterLook, cycleSiteChromeLookId, usePreviewThemeOverlay, resolvePreviewDashboardSetupHandoff, buildPreviewDashboardSetupUrl, resolvePreviewSetupBannerModel, planSiteChromeLookSelection, sanitizeDashboardPath } from './chunk-LCY32PXF.mjs';
4
- import { resolvePreviewChromeSurface, getPreviewSidebarTab, onPreviewSidebarOpenRequest, getPreviewChromeSurfaceDataProps, PREVIEW_CHROME_THEME_CLASS_NAME, setPreviewSidebarTab, PreviewPageDesignEditorStateProvider, usePreviewPageDesignEditorState, PREVIEW_SURFACE_PARAM } from './chunk-GP555I66.mjs';
5
- import { clientComponentRegistry, deriveLaunchReadiness, DEFAULT_LAUNCH_READINESS_INPUT, useEditorUiStore, ResizableSplitPane, BlockApiProvider, EditorProvider, MediaEditorProvider, getCuratedSiteStyle, useEditor, createRoutingHelpers, createSnapshotFromPageRecord, EntryControllerProvider, useEntryEditorController, useEditorUiStoreInstance, surfaceResolutionContextFromTheme, unsafeAsEditorBlockId, EditableBlockProvider, asDesignBlockId, PageRenderer, planSiteDocument, ThemeScope, FixedBottomSurface, Button, unwrapResponse, X, ChevronLeft, ChevronRight, RotateCcw, parseBackgroundOverrideFromContent, rankBackgroundImagePoolItems, imagePoolForOverride, resolveBackgroundCapability, enabledBackgroundModes, PreviewRichTextEditor, RichText, PreviewTextEditor, Text, buildThemeRuntime, renderBlock, siteHeaderManifest, siteFooterManifest, SiteDocumentLayout, SITE_CHROME_CLASS_NAMES, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, buildEditorModelRuntimeFieldsByBlockId, applyCompiledRuntimeFieldsToContent, assertNever2, getBlockDefinition, systemCustomizeFacetDefinitionsForBlock, systemCustomizeFacetAppliesToLayoutVariant, systemCustomizeFacetCurrentValue, systemCustomizeFacetLabelForValue, Image, useMediaEditorOptional, defaultBackgroundOverrideForMode, Upload, MediaEditor, visibleHeaderClearancePx, systemCustomizeFacetNextValue, GRADIENT_PRESETS, planBackgroundTransition, parseMediaAssetId, planBackgroundOverride, EDITABLE_SURFACE_FORWARDING_PROP, assertNever, SITE_CHROME_VISIBLE_HEADER_SELECTOR } from './chunk-V47ZU33I.mjs';
6
- import { API_ENDPOINTS, createBearerAPIClient, isPreviewRuntimeSessionEditable, DEFAULT_SURFACE_ID, initPreviewAuth, usePreviewAuth, usePreviewAuthFetch, markPreviewAuthExpired, usePreviewToken, parseSurfaceId } from './chunk-K2IIF34C.mjs';
7
- import React9, { createContext, useMemo, useState, useEffect, useRef, useCallback, useId, useReducer, Children, isValidElement, Fragment as Fragment$1, cloneElement, useContext } from 'react';
2
+ import { useOptionalPreviewLayout, PreviewQueryProvider } from './chunk-4T5JIPWY.mjs';
3
+ import { PreviewBlockCustomizeSelectionProvider, resolveSiteStyleAccess, resolvePreviewDashboardSetupHandoff, buildPreviewDashboardSetupUrl, resolvePreviewSetupBannerModel, usePreviewBlockCustomizeSelectionOptional, useBlockCustomizeControls, MAX_CUSTOMIZE_POPOVER_ROWS, MAX_QUICK_MANIFEST_FIELDS, sanitizeDashboardPath } from './chunk-3QWQ2MUQ.mjs';
4
+ import { resolvePreviewChromeSurface, getPreviewSidebarTab, onPreviewSidebarOpenRequest, getPreviewChromeSurfaceDataProps, PREVIEW_CHROME_THEME_CLASS_NAME, setPreviewSidebarTab, PreviewPageDesignEditorStateProvider, usePreviewPageDesignEditorState } from './chunk-3VT7LZY2.mjs';
5
+ import { clientComponentRegistry, deriveLaunchReadiness, DEFAULT_LAUNCH_READINESS_INPUT, useEditorUiStore, PreviewThemeOverlayProvider, PreviewDesignCoordinatorProvider, SiteChromeCustomizeProvider, ResizableSplitPane, BlockApiProvider, EditorProvider, PreviewDesignHistoryOrderProvider, MediaEditorProvider, useEditorUiStoreInstance, useEditor, createRoutingHelpers, createSnapshotFromPageRecord, EntryControllerProvider, useEntryEditorController, surfaceResolutionContextFromTheme, unsafeAsEditorBlockId, EditableBlockProvider, asDesignBlockId, PageRenderer, usePreviewThemeOverlay, planSiteDocument, ThemeScope, FixedBottomSurface, Button, sectionTransitionVariantForBoundaryOptionId, sectionTransitionFamilyForBoundaryOptionId, sectionTransitionFamilies, X, defaultSectionTransitionVariantForFamily, RotateCcw, parseBackgroundOverrideFromContent, rankBackgroundImagePoolItems, imagePoolForOverride, resolveBackgroundCapability, enabledBackgroundModes, FormRenderer, stableSerialize, prefetchBlockData, isSupportedLoaderEndpoint, PreviewRichTextEditor, RichText, PreviewTextEditor, Text, buildThemeRuntime, renderBlock, siteHeaderManifest, siteFooterManifest, SiteDocumentLayout, SITE_CHROME_CLASS_NAMES, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, buildEditorModelRuntimeFieldsByBlockId, applyCompiledRuntimeFieldsToContent, assertNever2, getBlockDefinition, systemCustomizeFacetDefinitionsForBlock, systemCustomizeFacetAppliesToLayoutVariant, systemCustomizeFacetCurrentValue, systemCustomizeFacetLabelForValue, backgroundColorTokens, ChevronLeft, ChevronRight, createMicrobatcher, unwrapResponse, normalizePublicApiLoaderParams, visibleHeaderClearancePx, LINKED_CONTENT_ENTRY_META_KEY, unsafeAsLinkedContentEntryId, useMediaEditorOptional, Image, defaultBackgroundOverrideForMode, decodePublicProductCategorySelectorQuery, EDITABLE_SURFACE_FORWARDING_PROP, useSiteChromeCustomizeOptional, assertNever, SITE_CHROME_VISIBLE_HEADER_SELECTOR, GRADIENT_PRESETS, resolveImageUrlWithContext, gradientToCss, Upload, MediaEditor, SECTION_BACKGROUND_MIN_SOURCE_WIDTH, SECTION_BACKGROUND_MIN_SOURCE_HEIGHT, parseMediaAssetId, planBackgroundOverride, planBackgroundTransition, systemCustomizeFacetNextValue } from './chunk-WUHKQBB6.mjs';
6
+ import { API_ENDPOINTS, createBearerAPIClient, isPreviewRuntimeSessionEditable, DEFAULT_SURFACE_ID, initPreviewAuth, usePreviewAuth, usePreviewAuthFetch, markPreviewAuthExpired, usePreviewToken } from './chunk-E3NZYEKF.mjs';
7
+ import React10, { createContext, useMemo, useState, useEffect, useRef, useCallback, useId, useReducer, Children, isValidElement, Fragment as Fragment$1, cloneElement, useContext } from 'react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import { createPortal } from 'react-dom';
9
10
 
10
11
  // ../preview-next/src/client/utils/path.ts
11
12
  var PATH_SEPARATOR_REGEX = /\./g;
@@ -252,12 +253,90 @@ function blockHasPreviewContentFieldProjection(blockId, projections) {
252
253
  );
253
254
  }
254
255
 
256
+ // ../preview-next/src/client/blocks/linkedEntryRenderOverlay.ts
257
+ function applyLinkedEntryRenderOverlay(value, overlays) {
258
+ if (overlays.size === 0) return value;
259
+ return overlayValue(value, overlays);
260
+ }
261
+ function applyLinkedEntryRenderOverlayToDataContext(dataContext, overlays) {
262
+ if (!dataContext?.resolvedData || overlays.size === 0) {
263
+ return dataContext;
264
+ }
265
+ const resolvedData = applyLinkedEntryRenderOverlay(
266
+ dataContext.resolvedData,
267
+ overlays
268
+ );
269
+ if (resolvedData === dataContext.resolvedData) {
270
+ return dataContext;
271
+ }
272
+ return {
273
+ ...dataContext,
274
+ resolvedData
275
+ };
276
+ }
277
+ function overlayValue(value, overlays) {
278
+ if (Array.isArray(value)) {
279
+ let changed2 = false;
280
+ const next2 = value.map((item) => {
281
+ const overlaid = overlayValue(item, overlays);
282
+ if (overlaid !== item) changed2 = true;
283
+ return overlaid;
284
+ });
285
+ return changed2 ? next2 : value;
286
+ }
287
+ if (!isRecord3(value)) return value;
288
+ const meta = parseLinkedEntryMeta(value[LINKED_CONTENT_ENTRY_META_KEY]);
289
+ if (meta) {
290
+ const overlay = overlays.get(unsafeAsLinkedContentEntryId(meta.entryId));
291
+ if (overlay) {
292
+ const title = resolveOverlayTitle(overlay.content, meta);
293
+ return {
294
+ ...value,
295
+ title,
296
+ content: overlay.content,
297
+ optimisticHash: overlay.optimisticHash,
298
+ [LINKED_CONTENT_ENTRY_META_KEY]: {
299
+ ...meta,
300
+ title,
301
+ optimisticHash: overlay.optimisticHash
302
+ }
303
+ };
304
+ }
305
+ }
306
+ let changed = false;
307
+ const next = {};
308
+ for (const [key, child] of Object.entries(value)) {
309
+ const overlaid = overlayValue(child, overlays);
310
+ if (overlaid !== child) changed = true;
311
+ next[key] = overlaid;
312
+ }
313
+ return changed ? next : value;
314
+ }
315
+ function resolveOverlayTitle(content, meta) {
316
+ const titleField = meta.contentType.titleField;
317
+ if (titleField) {
318
+ const value = content[titleField];
319
+ if (typeof value === "string" && value.trim().length > 0) {
320
+ return value;
321
+ }
322
+ }
323
+ return meta.title;
324
+ }
325
+ function parseLinkedEntryMeta(value) {
326
+ if (!isRecord3(value)) return null;
327
+ return value.kind === "linked-content-entry" && typeof value.entryId === "string" ? value : null;
328
+ }
329
+ function isRecord3(value) {
330
+ return typeof value === "object" && value !== null && !Array.isArray(value);
331
+ }
332
+
255
333
  // ../preview-next/src/client/blocks/composer/mergeDrafts.ts
334
+ var EMPTY_LINKED_ENTRY_OVERLAY = /* @__PURE__ */ new Map();
256
335
  function getDraftSnapshots(uiStore) {
257
336
  const snapshots = uiStore.getState().drafts.inspect();
258
337
  return new Map(snapshots.map((snap) => [snap.id, snap]));
259
338
  }
260
- function mergeBlockWithDraft(block, draftMap, index, projections = []) {
339
+ function mergeBlockWithDraft(block, draftMap, index, projections = [], linkedEntryOverlays = EMPTY_LINKED_ENTRY_OVERLAY) {
261
340
  const id = block.id ?? null;
262
341
  const editorBlockId = id ? unsafeAsEditorBlockId(id) : null;
263
342
  const snap = editorBlockId ? draftMap.get(editorBlockId) ?? null : null;
@@ -269,20 +348,22 @@ function mergeBlockWithDraft(block, draftMap, index, projections = []) {
269
348
  kind: block.kind,
270
349
  purpose: "content",
271
350
  orderIndex: block.orderIndex ?? index,
272
- content: renderContent(block, baseline, projections),
273
- draftContent: renderContent(block, draftContent, projections),
351
+ content: renderContent(block, baseline, projections, linkedEntryOverlays),
352
+ draftContent: renderContent(block, draftContent, projections, linkedEntryOverlays),
274
353
  publishedContent: renderContent(
275
354
  block,
276
355
  block.publishedContent ?? null,
277
- projections
356
+ projections,
357
+ EMPTY_LINKED_ENTRY_OVERLAY
278
358
  ),
279
359
  bindings: block.bindings ?? null
280
360
  };
281
361
  }
282
- function renderContent(block, content, projections) {
362
+ function renderContent(block, content, projections, linkedEntryOverlays) {
363
+ const overlaidContent = applyLinkedEntryRenderOverlay(content, linkedEntryOverlays);
283
364
  return applyPreviewRuntimeContentProjections(
284
365
  block,
285
- content,
366
+ overlaidContent,
286
367
  projections
287
368
  );
288
369
  }
@@ -333,18 +414,20 @@ function composePageFromBlocks(args, cacheRef) {
333
414
  const aiProjections = previewProjectionsFromAiOverlays(
334
415
  uiStore.getState().aiOverlays
335
416
  );
417
+ const linkedEntryOverlays = getLinkedEntryRenderOverlays(uiStore);
336
418
  return composePreviewRenderPage(
337
419
  {
338
420
  page,
339
421
  blockItems,
340
422
  draftSnapshots: Array.from(draftMap.values()),
341
- projections: [...aiProjections, ...projections]
423
+ projections: [...aiProjections, ...projections],
424
+ linkedEntryOverlays
342
425
  },
343
426
  cacheRef
344
427
  );
345
428
  }
346
429
  function composePreviewRenderPage(args, cacheRef) {
347
- const { page, blockItems, draftSnapshots, projections } = args;
430
+ const { page, blockItems, draftSnapshots, projections, linkedEntryOverlays } = args;
348
431
  if (!page) return page;
349
432
  if (!blockItems.length) return page;
350
433
  const draftMap = draftSnapshotMap(draftSnapshots);
@@ -353,7 +436,7 @@ function composePreviewRenderPage(args, cacheRef) {
353
436
  projections
354
437
  );
355
438
  const enrichedBlocks = projectedBlockItems.map(
356
- (block, index) => mergeBlockWithDraft(block, draftMap, index, projections)
439
+ (block, index) => mergeBlockWithDraft(block, draftMap, index, projections, linkedEntryOverlays)
357
440
  );
358
441
  const sortedBlocks = [...enrichedBlocks].sort(
359
442
  (a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0)
@@ -361,9 +444,35 @@ function composePreviewRenderPage(args, cacheRef) {
361
444
  const renderedBlocks = applyCacheOptimization(sortedBlocks, cacheRef);
362
445
  return { ...page, blocks: renderedBlocks };
363
446
  }
447
+ function getLinkedEntryRenderOverlays(uiStore) {
448
+ const linkedEntries = uiStore.getState().linkedEntries.inspect();
449
+ const overlays = /* @__PURE__ */ new Map();
450
+ for (const linkedEntry of linkedEntries) {
451
+ const content = linkedEntry.current ?? linkedEntry.baseline;
452
+ if (!content) continue;
453
+ overlays.set(linkedEntry.meta.entryId, {
454
+ content,
455
+ optimisticHash: linkedEntry.meta.optimisticHash
456
+ });
457
+ }
458
+ return overlays;
459
+ }
364
460
  function draftSnapshotMap(snapshots) {
365
461
  return new Map(snapshots.map((snap) => [snap.id, snap]));
366
462
  }
463
+
464
+ // ../preview-next/src/client/preview/linkedEntryEditEvents.ts
465
+ var LINKED_ENTRY_EDIT_EVENT = "riverbank:preview:linked-entry-edit";
466
+ function dispatchLinkedEntryEditEvent(target) {
467
+ window.dispatchEvent(new CustomEvent(LINKED_ENTRY_EDIT_EVENT, {
468
+ detail: target
469
+ }));
470
+ }
471
+ function isLinkedEntryEditEvent(event) {
472
+ if (!(event instanceof CustomEvent)) return false;
473
+ const detail = event.detail;
474
+ return Boolean(detail) && typeof detail === "object" && detail.kind === "linked-content-entry";
475
+ }
367
476
  function requireClientRegistryComponent(key) {
368
477
  const component = clientComponentRegistry[key];
369
478
  if (!component) {
@@ -388,6 +497,37 @@ var RichTextWithEditor = ({ value, path, ...rest }) => {
388
497
  }
389
498
  return /* @__PURE__ */ jsx(RichText, { ...rest, path, doc: value });
390
499
  };
500
+ var LinkedEntryEditFrame = ({ target, children }) => {
501
+ const { isEditingMode } = useEditor();
502
+ if (!isEditingMode) {
503
+ return /* @__PURE__ */ jsx(Fragment, { children });
504
+ }
505
+ return /* @__PURE__ */ jsxs(
506
+ "div",
507
+ {
508
+ className: "group/linked-entry-edit relative",
509
+ "data-linked-entry-id": target.entryId,
510
+ "data-linked-entry-type": target.contentTypeKey,
511
+ children: [
512
+ children,
513
+ /* @__PURE__ */ jsx(
514
+ "button",
515
+ {
516
+ type: "button",
517
+ "aria-label": `Edit ${target.contentTypeLabel}: ${target.title}`,
518
+ className: "absolute right-2 top-2 z-20 rounded-full border border-white/80 bg-neutral-950/85 px-3 py-1 text-xs font-medium text-white opacity-0 shadow-sm transition focus:opacity-100 group-hover/linked-entry-edit:opacity-100",
519
+ onClick: (event) => {
520
+ event.preventDefault();
521
+ event.stopPropagation();
522
+ dispatchLinkedEntryEditEvent(target);
523
+ },
524
+ children: "Edit"
525
+ }
526
+ )
527
+ ]
528
+ }
529
+ );
530
+ };
391
531
  var ButtonWithEditorInterception = (props) => {
392
532
  const { isEditingMode } = useEditor();
393
533
  if (!isEditingMode) {
@@ -411,14 +551,599 @@ var editorRegistry = {
411
551
  text: TextWithEditor,
412
552
  richText: RichTextWithEditor,
413
553
  button: ButtonWithEditorInterception,
414
- media: requireClientRegistryComponent("media")
554
+ media: requireClientRegistryComponent("media"),
555
+ linkedEntryEditFrame: LinkedEntryEditFrame
415
556
  };
416
557
 
558
+ // ../preview-next/src/client/preview/previewChromePlacement.ts
559
+ var PREVIEW_CHROME_EDGE_OFFSET_PX = 8;
560
+ var DEFAULT_PREVIEW_CHROME_FLOATING_PLACEMENT = Object.freeze({
561
+ kind: "anchor-top-right",
562
+ topPx: PREVIEW_CHROME_EDGE_OFFSET_PX,
563
+ rightPx: PREVIEW_CHROME_EDGE_OFFSET_PX
564
+ });
565
+ function headerClearanceFromPx(px) {
566
+ return Number.isFinite(px) && px > 0 ? { kind: "overlay", px: Math.ceil(px) } : { kind: "none" };
567
+ }
568
+ function findFirstEditableContentBlockIndex(blocks) {
569
+ const index = blocks.findIndex((block) => block.kind !== "block.site-header");
570
+ return index >= 0 ? index : null;
571
+ }
572
+ function blockReceivesHeaderClearance(blocks, blockIndex) {
573
+ return findFirstEditableContentBlockIndex(blocks) === blockIndex;
574
+ }
575
+ function planBlockToolbarPlacement(input) {
576
+ if (input.headerClearance.kind === "overlay" && blockReceivesHeaderClearance(input.blocks, input.blockIndex)) {
577
+ return {
578
+ kind: "anchor-top-right",
579
+ topPx: input.headerClearance.px + PREVIEW_CHROME_EDGE_OFFSET_PX,
580
+ rightPx: PREVIEW_CHROME_EDGE_OFFSET_PX
581
+ };
582
+ }
583
+ return {
584
+ kind: "anchor-top-right",
585
+ topPx: PREVIEW_CHROME_EDGE_OFFSET_PX,
586
+ rightPx: PREVIEW_CHROME_EDGE_OFFSET_PX
587
+ };
588
+ }
589
+ function planSiteChromeCustomizePlacement(input) {
590
+ if (input.surface === "header" && !isUsableAnchorBox(input.anchorBox)) {
591
+ return {
592
+ kind: "viewport-top-right",
593
+ topPx: PREVIEW_CHROME_EDGE_OFFSET_PX * 2,
594
+ rightPx: PREVIEW_CHROME_EDGE_OFFSET_PX * 2
595
+ };
596
+ }
597
+ return {
598
+ kind: "anchor-top-right",
599
+ topPx: PREVIEW_CHROME_EDGE_OFFSET_PX,
600
+ rightPx: PREVIEW_CHROME_EDGE_OFFSET_PX
601
+ };
602
+ }
603
+ function resolvePreviewChromeFloatingPosition(input) {
604
+ if (input.placement.kind === "viewport-top-right") {
605
+ return {
606
+ topPx: input.placement.topPx,
607
+ rightPx: input.placement.rightPx
608
+ };
609
+ }
610
+ const anchorBox = input.anchorBox;
611
+ if (!isUsableAnchorBox(anchorBox)) {
612
+ return {
613
+ topPx: input.placement.topPx,
614
+ rightPx: input.placement.rightPx
615
+ };
616
+ }
617
+ return {
618
+ topPx: Math.max(
619
+ PREVIEW_CHROME_EDGE_OFFSET_PX,
620
+ anchorBox.top - (input.containerTopEdgePx ?? 0) + input.placement.topPx
621
+ ),
622
+ rightPx: Math.max(
623
+ PREVIEW_CHROME_EDGE_OFFSET_PX,
624
+ input.containerRightEdgePx - anchorBox.right + input.placement.rightPx
625
+ )
626
+ };
627
+ }
628
+ function isUsableAnchorBox(box) {
629
+ return Boolean(
630
+ box && Number.isFinite(box.top) && Number.isFinite(box.right) && Number.isFinite(box.bottom) && Number.isFinite(box.height) && box.height > 0
631
+ );
632
+ }
633
+ var PreviewChromeLayerContext = createContext(null);
634
+ function PreviewChromeLayerProvider({
635
+ children
636
+ }) {
637
+ const [portalTarget, setPortalTarget] = useState(null);
638
+ const listenersRef = useRef(/* @__PURE__ */ new Set());
639
+ const notifyLayout = useCallback(() => {
640
+ listenersRef.current.forEach((listener) => listener());
641
+ }, []);
642
+ const subscribeLayout = useCallback((listener) => {
643
+ listenersRef.current.add(listener);
644
+ return () => {
645
+ listenersRef.current.delete(listener);
646
+ };
647
+ }, []);
648
+ useEffect(() => {
649
+ let frame = null;
650
+ const scheduleNotify = () => {
651
+ if (frame !== null) return;
652
+ frame = window.requestAnimationFrame(() => {
653
+ frame = null;
654
+ notifyLayout();
655
+ });
656
+ };
657
+ const mutationObserver = typeof MutationObserver !== "undefined" ? new MutationObserver(scheduleNotify) : null;
658
+ mutationObserver?.observe(document.body, {
659
+ attributes: true,
660
+ childList: true,
661
+ subtree: true
662
+ });
663
+ window.addEventListener("resize", scheduleNotify);
664
+ window.addEventListener("scroll", scheduleNotify, true);
665
+ return () => {
666
+ if (frame !== null) {
667
+ window.cancelAnimationFrame(frame);
668
+ }
669
+ mutationObserver?.disconnect();
670
+ window.removeEventListener("resize", scheduleNotify);
671
+ window.removeEventListener("scroll", scheduleNotify, true);
672
+ };
673
+ }, [notifyLayout]);
674
+ const value = useMemo(
675
+ () => ({ portalTarget, subscribeLayout }),
676
+ [portalTarget, subscribeLayout]
677
+ );
678
+ return /* @__PURE__ */ jsxs(PreviewChromeLayerContext.Provider, { value, children: [
679
+ children,
680
+ /* @__PURE__ */ jsx(
681
+ "div",
682
+ {
683
+ ref: setPortalTarget,
684
+ className: "pointer-events-none fixed inset-0 z-[70]",
685
+ "data-preview-chrome-layer": ""
686
+ }
687
+ )
688
+ ] });
689
+ }
690
+ function usePreviewChromeLayerOptional() {
691
+ return useContext(PreviewChromeLayerContext);
692
+ }
693
+ function PreviewChromeFloatingControl({
694
+ anchorSelector,
695
+ placement,
696
+ fallbackClassName,
697
+ fallbackStyle,
698
+ portalClassName = "absolute z-[70] flex gap-1 transition",
699
+ forceVisible = false,
700
+ children
701
+ }) {
702
+ const layer = usePreviewChromeLayerOptional();
703
+ const probeRef = useRef(null);
704
+ const anchorElementRef = useRef(null);
705
+ const [controlElement, setControlElement] = useState(
706
+ null
707
+ );
708
+ const [anchorElement, setAnchorElement] = useState(null);
709
+ const [anchorBox, setAnchorBox] = useState(
710
+ null
711
+ );
712
+ const [themeCustomProperties, setThemeCustomProperties] = useState({});
713
+ const [isActive, setIsActive] = useState(false);
714
+ const hoverStateRef = useRef({ anchor: false, control: false });
715
+ useEffect(() => {
716
+ if (!layer?.portalTarget) return;
717
+ let resizeObserver = null;
718
+ const readAnchor = () => probeRef.current?.closest(anchorSelector);
719
+ const update = () => {
720
+ const anchor = readAnchor();
721
+ if (anchor !== anchorElementRef.current) {
722
+ resizeObserver?.disconnect();
723
+ resizeObserver = null;
724
+ anchorElementRef.current = anchor;
725
+ setAnchorElement(anchor);
726
+ if (anchor && typeof ResizeObserver !== "undefined") {
727
+ resizeObserver = new ResizeObserver(update);
728
+ resizeObserver.observe(anchor);
729
+ }
730
+ }
731
+ if (!anchor) {
732
+ setAnchorBox(null);
733
+ setThemeCustomProperties({});
734
+ return;
735
+ }
736
+ const rect = anchor.getBoundingClientRect();
737
+ setThemeCustomProperties(readThemeCustomProperties(anchor));
738
+ setAnchorBox({
739
+ top: rect.top,
740
+ right: rect.right,
741
+ bottom: rect.bottom,
742
+ height: rect.height
743
+ });
744
+ };
745
+ update();
746
+ const unsubscribe = layer.subscribeLayout(update);
747
+ return () => {
748
+ resizeObserver?.disconnect();
749
+ anchorElementRef.current = null;
750
+ unsubscribe();
751
+ };
752
+ }, [anchorSelector, layer]);
753
+ useEffect(() => {
754
+ if (!anchorElement || !controlElement) return;
755
+ hoverStateRef.current = { anchor: false, control: false };
756
+ const show = (event) => {
757
+ updatePreviewChromeHoverState(
758
+ hoverStateRef.current,
759
+ event.currentTarget,
760
+ anchorElement,
761
+ controlElement,
762
+ true
763
+ );
764
+ setIsActive(true);
765
+ };
766
+ const hideIfFocusLeft = (event) => {
767
+ updatePreviewChromeHoverState(
768
+ hoverStateRef.current,
769
+ event.currentTarget,
770
+ anchorElement,
771
+ controlElement,
772
+ false
773
+ );
774
+ window.setTimeout(() => {
775
+ const active = document.activeElement;
776
+ const hoverState = hoverStateRef.current;
777
+ const nextIsActive = Boolean(
778
+ hoverState.anchor || hoverState.control || active && (anchorElement.contains(active) || controlElement.contains(active))
779
+ );
780
+ setIsActive(nextIsActive);
781
+ }, 0);
782
+ };
783
+ anchorElement.addEventListener("mouseenter", show);
784
+ anchorElement.addEventListener("mouseleave", hideIfFocusLeft);
785
+ anchorElement.addEventListener("focusin", show);
786
+ anchorElement.addEventListener("focusout", hideIfFocusLeft);
787
+ controlElement.addEventListener("mouseenter", show);
788
+ controlElement.addEventListener("mouseleave", hideIfFocusLeft);
789
+ controlElement.addEventListener("focusin", show);
790
+ controlElement.addEventListener("focusout", hideIfFocusLeft);
791
+ return () => {
792
+ anchorElement.removeEventListener("mouseenter", show);
793
+ anchorElement.removeEventListener("mouseleave", hideIfFocusLeft);
794
+ anchorElement.removeEventListener("focusin", show);
795
+ anchorElement.removeEventListener("focusout", hideIfFocusLeft);
796
+ controlElement.removeEventListener("mouseenter", show);
797
+ controlElement.removeEventListener("mouseleave", hideIfFocusLeft);
798
+ controlElement.removeEventListener("focusin", show);
799
+ controlElement.removeEventListener("focusout", hideIfFocusLeft);
800
+ };
801
+ }, [anchorElement, controlElement]);
802
+ if (!layer?.portalTarget) {
803
+ return /* @__PURE__ */ jsx("div", { className: fallbackClassName, style: fallbackStyle, children });
804
+ }
805
+ const resolvedPlacement = typeof placement === "function" ? placement(anchorBox) : placement;
806
+ const position = resolvePreviewChromeFloatingPosition({
807
+ anchorBox,
808
+ containerTopEdgePx: resolvePortalTopEdgePx(layer.portalTarget),
809
+ containerRightEdgePx: resolvePortalRightEdgePx(layer.portalTarget),
810
+ placement: resolvedPlacement
811
+ });
812
+ const isVisible = forceVisible || isActive;
813
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
814
+ /* @__PURE__ */ jsx("span", { ref: probeRef, "aria-hidden": "true", style: { display: "contents" } }),
815
+ createPortal(
816
+ /* @__PURE__ */ jsx(
817
+ "div",
818
+ {
819
+ ref: setControlElement,
820
+ className: joinClassNames(
821
+ portalClassName,
822
+ isVisible ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
823
+ ),
824
+ style: {
825
+ ...themeCustomProperties,
826
+ top: position.topPx,
827
+ right: position.rightPx
828
+ },
829
+ children
830
+ }
831
+ ),
832
+ layer.portalTarget
833
+ )
834
+ ] });
835
+ }
836
+ function readThemeCustomProperties(element) {
837
+ if (typeof window === "undefined") return {};
838
+ const computedStyle = window.getComputedStyle(element);
839
+ const properties = {};
840
+ copyThemeCustomProperties(computedStyle, properties);
841
+ for (let current = element; current; current = current.parentElement) {
842
+ copyThemeCustomProperties(current.style, properties);
843
+ }
844
+ return properties;
845
+ }
846
+ function copyThemeCustomProperties(style, target) {
847
+ for (let index = 0; index < style.length; index += 1) {
848
+ const propertyName = style.item(index);
849
+ if (!propertyName.startsWith("--tb-")) continue;
850
+ const propertyValue = style.getPropertyValue(propertyName).trim();
851
+ if (propertyValue.length > 0 && target[propertyName] === void 0) {
852
+ target[propertyName] = propertyValue;
853
+ }
854
+ }
855
+ }
856
+ function resolvePortalRightEdgePx(portalTarget) {
857
+ const rect = portalTarget.getBoundingClientRect();
858
+ if (Number.isFinite(rect.right) && rect.right > 0) {
859
+ return rect.right;
860
+ }
861
+ if (typeof window === "undefined") {
862
+ return 0;
863
+ }
864
+ return window.innerWidth;
865
+ }
866
+ function resolvePortalTopEdgePx(portalTarget) {
867
+ const rect = portalTarget.getBoundingClientRect();
868
+ return Number.isFinite(rect.top) ? rect.top : 0;
869
+ }
870
+ function joinClassNames(...values) {
871
+ const className = values.filter(Boolean).join(" ");
872
+ return className.length > 0 ? className : void 0;
873
+ }
874
+ function updatePreviewChromeHoverState(hoverState, currentTarget, anchorElement, controlElement, value) {
875
+ if (currentTarget === anchorElement) {
876
+ hoverState.anchor = value;
877
+ } else if (currentTarget === controlElement) {
878
+ hoverState.control = value;
879
+ }
880
+ }
881
+ function useDialogFocusTrap(input) {
882
+ const { open, onClose } = input;
883
+ const dialogRef = useRef(null);
884
+ const initialFocusRef = useRef(null);
885
+ const returnFocusRef = useRef(null);
886
+ useEffect(() => {
887
+ if (open) {
888
+ initialFocusRef.current?.focus();
889
+ return;
890
+ }
891
+ const returnFocusTarget = returnFocusRef.current;
892
+ returnFocusRef.current = null;
893
+ returnFocusTarget?.focus();
894
+ }, [open]);
895
+ const registerInitialFocus = useCallback((node) => {
896
+ initialFocusRef.current = node;
897
+ }, []);
898
+ const setReturnFocusTarget = useCallback((node) => {
899
+ returnFocusRef.current = node;
900
+ }, []);
901
+ const handleDialogKeyDown = useCallback(
902
+ (event) => {
903
+ if (event.key === "Escape") {
904
+ event.preventDefault();
905
+ onClose();
906
+ return;
907
+ }
908
+ if (event.key !== "Tab") return;
909
+ const focusableElements = focusableElementsIn(dialogRef.current);
910
+ if (focusableElements.length === 0) {
911
+ event.preventDefault();
912
+ return;
913
+ }
914
+ const firstElement = focusableElements[0];
915
+ const lastElement = focusableElements[focusableElements.length - 1];
916
+ if (!firstElement || !lastElement) return;
917
+ if (event.shiftKey && document.activeElement === firstElement) {
918
+ event.preventDefault();
919
+ lastElement.focus();
920
+ return;
921
+ }
922
+ if (!event.shiftKey && document.activeElement === lastElement) {
923
+ event.preventDefault();
924
+ firstElement.focus();
925
+ }
926
+ },
927
+ [onClose]
928
+ );
929
+ return {
930
+ dialogRef,
931
+ registerInitialFocus,
932
+ setReturnFocusTarget,
933
+ handleDialogKeyDown
934
+ };
935
+ }
936
+ function focusableElementsIn(root) {
937
+ if (!root) return [];
938
+ return Array.from(
939
+ root.querySelectorAll(
940
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
941
+ )
942
+ ).filter((element) => !element.hasAttribute("disabled") && element.getAttribute("aria-hidden") !== "true");
943
+ }
944
+
417
945
  // ../preview-next/src/client/preview/previewCustomizeControlStyles.ts
418
946
  var previewCustomizePillButtonClassName = "rounded-full bg-black/80 px-2.5 py-1 text-xs text-white shadow hover:bg-black";
947
+ function useCustomizePopoverController() {
948
+ const panelId = useId();
949
+ const descriptionId = `${panelId}-description`;
950
+ const [open, setOpen] = useState(false);
951
+ const buttonRef = useRef(null);
952
+ const close = useCallback(() => {
953
+ setOpen(false);
954
+ }, []);
955
+ const {
956
+ dialogRef,
957
+ registerInitialFocus,
958
+ setReturnFocusTarget,
959
+ handleDialogKeyDown
960
+ } = useDialogFocusTrap({
961
+ open,
962
+ onClose: close
963
+ });
964
+ const toggle = useCallback(
965
+ (event) => {
966
+ event.preventDefault();
967
+ event.stopPropagation();
968
+ if (!open) {
969
+ setReturnFocusTarget(
970
+ event.currentTarget instanceof HTMLElement ? event.currentTarget : buttonRef.current
971
+ );
972
+ }
973
+ setOpen((current) => !current);
974
+ },
975
+ [open, setReturnFocusTarget]
976
+ );
977
+ return {
978
+ open,
979
+ close,
980
+ toggle,
981
+ buttonRef,
982
+ panelId,
983
+ descriptionId,
984
+ dialogRef,
985
+ registerInitialFocus,
986
+ handleDialogKeyDown
987
+ };
988
+ }
989
+ function CustomizePillButton({
990
+ buttonRef,
991
+ ariaLabel,
992
+ open,
993
+ controlsId,
994
+ onClick,
995
+ disabled = false
996
+ }) {
997
+ return /* @__PURE__ */ jsx(
998
+ "button",
999
+ {
1000
+ ref: buttonRef,
1001
+ type: "button",
1002
+ onClick,
1003
+ className: previewCustomizePillButtonClassName,
1004
+ "aria-label": ariaLabel,
1005
+ "aria-expanded": open,
1006
+ "aria-haspopup": "dialog",
1007
+ "aria-controls": open ? controlsId : void 0,
1008
+ disabled,
1009
+ children: "Customize"
1010
+ }
1011
+ );
1012
+ }
1013
+ function CustomizePopoverShell({
1014
+ panelId,
1015
+ descriptionId,
1016
+ dialogLabel,
1017
+ eyebrow,
1018
+ title,
1019
+ dialogRef,
1020
+ closeButtonRef,
1021
+ onClose,
1022
+ onKeyDown,
1023
+ spacing = "normal",
1024
+ children
1025
+ }) {
1026
+ return /* @__PURE__ */ jsxs(
1027
+ "div",
1028
+ {
1029
+ id: panelId,
1030
+ className: "builder-dashboard-chrome-theme fixed inset-x-3 bottom-[calc(var(--builder-editor-chrome-offset,0px)+1rem)] z-[70] rounded-lg border bg-background p-3 text-foreground shadow-xl sm:absolute sm:inset-x-auto sm:bottom-auto sm:right-0 sm:top-9 sm:w-72",
1031
+ role: "dialog",
1032
+ "aria-modal": "true",
1033
+ "aria-label": dialogLabel,
1034
+ "aria-describedby": descriptionId,
1035
+ ref: dialogRef,
1036
+ onKeyDown,
1037
+ onClick: (event) => event.stopPropagation(),
1038
+ children: [
1039
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
1040
+ /* @__PURE__ */ jsxs("div", { children: [
1041
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: eyebrow }),
1042
+ /* @__PURE__ */ jsx("div", { id: descriptionId, className: "text-sm font-semibold", children: title })
1043
+ ] }),
1044
+ /* @__PURE__ */ jsx(
1045
+ "button",
1046
+ {
1047
+ ref: closeButtonRef,
1048
+ type: "button",
1049
+ className: "rounded-full px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
1050
+ onClick: (event) => {
1051
+ event.preventDefault();
1052
+ event.stopPropagation();
1053
+ onClose();
1054
+ },
1055
+ children: "Close"
1056
+ }
1057
+ )
1058
+ ] }),
1059
+ /* @__PURE__ */ jsx("div", { className: spacing === "compact" ? "space-y-2" : "space-y-3", children })
1060
+ ]
1061
+ }
1062
+ );
1063
+ }
1064
+ function CustomizeCycleRow({
1065
+ label,
1066
+ valueLabel,
1067
+ previousLabel,
1068
+ nextLabel,
1069
+ availability,
1070
+ onPrevious,
1071
+ onNext
1072
+ }) {
1073
+ const reasonId = useId();
1074
+ const disabled = availability.kind === "unavailable";
1075
+ const unavailableReason = availability.kind === "unavailable" ? cycleUnavailableReasonLabel(availability.reason) : null;
1076
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1077
+ /* @__PURE__ */ jsxs("div", { children: [
1078
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: label }),
1079
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: valueLabel })
1080
+ ] }),
1081
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
1082
+ unavailableReason ? /* @__PURE__ */ jsx("span", { id: reasonId, className: "sr-only", children: unavailableReason }) : null,
1083
+ /* @__PURE__ */ jsx(
1084
+ "button",
1085
+ {
1086
+ type: "button",
1087
+ onClick: (event) => {
1088
+ event.preventDefault();
1089
+ event.stopPropagation();
1090
+ onPrevious();
1091
+ },
1092
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1093
+ "aria-label": previousLabel,
1094
+ "aria-describedby": unavailableReason ? reasonId : void 0,
1095
+ title: unavailableReason ?? void 0,
1096
+ disabled,
1097
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
1098
+ }
1099
+ ),
1100
+ /* @__PURE__ */ jsx(
1101
+ "button",
1102
+ {
1103
+ type: "button",
1104
+ onClick: (event) => {
1105
+ event.preventDefault();
1106
+ event.stopPropagation();
1107
+ onNext();
1108
+ },
1109
+ className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1110
+ "aria-label": nextLabel,
1111
+ "aria-describedby": unavailableReason ? reasonId : void 0,
1112
+ title: unavailableReason ?? void 0,
1113
+ disabled,
1114
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
1115
+ }
1116
+ )
1117
+ ] })
1118
+ ] });
1119
+ }
1120
+ function cycleAvailability(input) {
1121
+ if (input.kind === "available") {
1122
+ return input;
1123
+ }
1124
+ return { kind: "unavailable", reason: input.kind };
1125
+ }
1126
+ function cycleUnavailableReasonLabel(reason) {
1127
+ if (reason === "preview-only") {
1128
+ return "Preview-only users cannot change this control.";
1129
+ }
1130
+ if (reason === "single-option") {
1131
+ return "There are no other options to cycle to.";
1132
+ }
1133
+ return reason;
1134
+ }
1135
+ function cycleAvailabilityFromFlags(input) {
1136
+ if (!input.applyAllowed) {
1137
+ return { kind: "unavailable", reason: "preview-only" };
1138
+ }
1139
+ if (!input.canCycle) {
1140
+ return { kind: "unavailable", reason: "single-option" };
1141
+ }
1142
+ return { kind: "available" };
1143
+ }
419
1144
 
420
1145
  // ../preview-next/src/client/blocks/isRecord.ts
421
- function isRecord3(value) {
1146
+ function isRecord4(value) {
422
1147
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
423
1148
  }
424
1149
 
@@ -484,35 +1209,18 @@ function setPageDesignBlockBackground(input) {
484
1209
  });
485
1210
  const plan = planBackgroundOverride(current, capability, input.override);
486
1211
  if (plan.kind !== "apply") return null;
1212
+ const imageAsset = plan.override.kind === "image" ? imagePoolAssetForMediaId(input.imagePool, plan.override.mediaId) : void 0;
487
1213
  return {
488
1214
  kind: "set-background",
489
1215
  blockId: input.block.blockId,
490
1216
  override: plan.override,
491
1217
  fromOverride: plan.fromOverride,
492
- baseContentId: input.block.contentRevision?.contentId ?? null
1218
+ baseContentId: input.block.contentRevision?.contentId ?? null,
1219
+ ...imageAsset ? { imageAsset } : {}
493
1220
  };
494
1221
  }
495
- function cyclePageDesignBoundaryBreak(boundary, direction) {
496
- if (boundary.availableBreaks.length === 0) return null;
497
- const breakIds = boundary.availableBreaks.map((option) => option.id);
498
- const values = [null, ...breakIds];
499
- const currentIndex = cycleChoiceIndex(boundary.breakChoice, values, direction);
500
- if (currentIndex === -1) return null;
501
- const nextIndex = direction === "next" ? (currentIndex + 1) % values.length : currentIndex === 0 ? values.length - 1 : currentIndex - 1;
502
- const boundaryOptionId = values[nextIndex];
503
- if (boundaryOptionId === void 0) return null;
504
- return {
505
- ...boundaryOptionId ? {
506
- kind: "set-boundary",
507
- previousBlockId: boundary.previousBlockId,
508
- nextBlockId: boundary.nextBlockId,
509
- boundaryOptionId
510
- } : {
511
- kind: "reset-boundary",
512
- previousBlockId: boundary.previousBlockId,
513
- nextBlockId: boundary.nextBlockId
514
- }
515
- };
1222
+ function imagePoolAssetForMediaId(imagePool, mediaId) {
1223
+ return imagePool?.find((item) => item.mediaId === mediaId)?.mediaAsset;
516
1224
  }
517
1225
  function cycleChoiceIndex(choice, values, direction) {
518
1226
  if (choice.kind === "user-pinned" && choice.status === "supported") {
@@ -543,68 +1251,37 @@ function previewCustomizeFacetControlsFor(blockKind, content, designBlock) {
543
1251
  };
544
1252
  });
545
1253
  }
546
- function useDialogFocusTrap(input) {
547
- const { open, onClose } = input;
548
- const dialogRef = useRef(null);
549
- const initialFocusRef = useRef(null);
550
- const returnFocusRef = useRef(null);
551
- useEffect(() => {
552
- if (open) {
553
- initialFocusRef.current?.focus();
554
- return;
555
- }
556
- const returnFocusTarget = returnFocusRef.current;
557
- returnFocusRef.current = null;
558
- returnFocusTarget?.focus();
559
- }, [open]);
560
- const registerInitialFocus = useCallback((node) => {
561
- initialFocusRef.current = node;
562
- }, []);
563
- const setReturnFocusTarget = useCallback((node) => {
564
- returnFocusRef.current = node;
565
- }, []);
566
- const handleDialogKeyDown = useCallback(
567
- (event) => {
568
- if (event.key === "Escape") {
569
- event.preventDefault();
570
- onClose();
571
- return;
572
- }
573
- if (event.key !== "Tab") return;
574
- const focusableElements = focusableElementsIn(dialogRef.current);
575
- if (focusableElements.length === 0) {
576
- event.preventDefault();
577
- return;
578
- }
579
- const firstElement = focusableElements[0];
580
- const lastElement = focusableElements[focusableElements.length - 1];
581
- if (!firstElement || !lastElement) return;
582
- if (event.shiftKey && document.activeElement === firstElement) {
583
- event.preventDefault();
584
- lastElement.focus();
585
- return;
586
- }
587
- if (!event.shiftKey && document.activeElement === lastElement) {
588
- event.preventDefault();
589
- firstElement.focus();
590
- }
591
- },
592
- [onClose]
1254
+
1255
+ // ../preview-next/src/client/blocks/blockCustomizePopoverRows.ts
1256
+ function buildBlockCustomizeBuiltinRows({
1257
+ designBlock,
1258
+ facetControls,
1259
+ hasLayoutControls,
1260
+ hasLookControls,
1261
+ hasBackgroundControls
1262
+ }) {
1263
+ const rows = [];
1264
+ if (designBlock?.layout && hasLayoutControls) {
1265
+ rows.push({ kind: "variant", block: designBlock });
1266
+ }
1267
+ if (designBlock?.layout?.status === "unsupported") {
1268
+ rows.push({ kind: "unsupported-variant-warning" });
1269
+ }
1270
+ if (designBlock && hasLookControls) {
1271
+ rows.push({ kind: "look", block: designBlock });
1272
+ }
1273
+ if (hasBackgroundControls) {
1274
+ rows.push({ kind: "background" });
1275
+ }
1276
+ rows.push(
1277
+ ...facetControls.map(
1278
+ (control) => ({ kind: "facet", control })
1279
+ )
593
1280
  );
594
- return {
595
- dialogRef,
596
- registerInitialFocus,
597
- setReturnFocusTarget,
598
- handleDialogKeyDown
599
- };
1281
+ return rows;
600
1282
  }
601
- function focusableElementsIn(root) {
602
- if (!root) return [];
603
- return Array.from(
604
- root.querySelectorAll(
605
- 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
606
- )
607
- ).filter((element) => !element.hasAttribute("disabled") && element.getAttribute("aria-hidden") !== "true");
1283
+ function blockCustomizeBuiltinRowBudget(rows) {
1284
+ return rows.length;
608
1285
  }
609
1286
  function isUnknownArray(value) {
610
1287
  return Array.isArray(value);
@@ -613,39 +1290,34 @@ function BlockToolbar({
613
1290
  blockId,
614
1291
  blockKind,
615
1292
  content,
616
- headerClearancePx = 0
1293
+ theme = null,
1294
+ placement = DEFAULT_PREVIEW_CHROME_FLOATING_PLACEMENT
617
1295
  }) {
618
1296
  const uiStore = useEditorUiStoreInstance();
619
- const customizePanelId = useId();
620
- const customizeDescriptionId = `${customizePanelId}-description`;
1297
+ const editorBlockId = useMemo(() => unsafeAsEditorBlockId(blockId), [blockId]);
1298
+ const draftContent = useEditorUiStore(
1299
+ (state) => state.drafts.getCurrentValues(editorBlockId)
1300
+ );
1301
+ const customizeContent = useMemo(
1302
+ () => isRecord4(draftContent) ? draftContent : content,
1303
+ [content, draftContent]
1304
+ );
1305
+ const customizeSelection = usePreviewBlockCustomizeSelectionOptional();
1306
+ const popover = useCustomizePopoverController();
621
1307
  const focusTarget = useMemo(
622
- () => computeDefaultFocus(blockKind, content),
623
- [blockKind, content]
1308
+ () => computeDefaultFocus(blockKind, customizeContent),
1309
+ [blockKind, customizeContent]
624
1310
  );
625
- const [customizeOpen, setCustomizeOpen] = useState(false);
626
1311
  const [backgroundChooserOpen, setBackgroundChooserOpen] = useState(false);
627
- const customizeButtonRef = useRef(null);
1312
+ const [backgroundColorPalette, setBackgroundColorPalette] = useState(null);
628
1313
  const {
629
1314
  state: pageDesignState,
630
1315
  applyAction: applyPageDesignAction,
631
- isHistoryPending,
632
1316
  historyFailure,
633
1317
  backgroundImagePool = [],
634
1318
  backgroundImagePoolStatus = "idle",
635
1319
  loadBackgroundImagePool = async () => []
636
1320
  } = usePreviewPageDesignEditorState();
637
- const closeCustomize = useCallback(() => {
638
- setCustomizeOpen(false);
639
- }, []);
640
- const {
641
- dialogRef: customizeDialogRef,
642
- registerInitialFocus: registerCustomizeInitialFocus,
643
- setReturnFocusTarget: setCustomizeReturnFocusTarget,
644
- handleDialogKeyDown: handleCustomizeDialogKeyDown
645
- } = useDialogFocusTrap({
646
- open: customizeOpen,
647
- onClose: closeCustomize
648
- });
649
1321
  const designBlock = useMemo(() => {
650
1322
  if (pageDesignState.kind !== "loaded") return null;
651
1323
  return pageDesignState.editorModel.blocks.find(
@@ -658,17 +1330,18 @@ function BlockToolbar({
658
1330
  const hasLookControls = Boolean(
659
1331
  designBlock && designBlock.availableLooks.length > 0
660
1332
  );
661
- const canCycleLook = Boolean(
662
- designBlock && designBlock.availableLooks.length > 1
663
- );
664
1333
  const facetControls = useMemo(
665
- () => previewCustomizeFacetControlsFor(blockKind, content, designBlock),
666
- [blockKind, content, designBlock]
1334
+ () => previewCustomizeFacetControlsFor(
1335
+ blockKind,
1336
+ customizeContent,
1337
+ designBlock
1338
+ ),
1339
+ [blockKind, customizeContent, designBlock]
667
1340
  );
668
1341
  const hasFacetControls = facetControls.length > 0;
669
1342
  const currentBackground = useMemo(
670
- () => parseBackgroundOverrideFromContent(content),
671
- [content]
1343
+ () => parseBackgroundOverrideFromContent(customizeContent),
1344
+ [customizeContent]
672
1345
  );
673
1346
  const mergedBackgroundImagePool = useMemo(
674
1347
  () => rankBackgroundImagePoolItems(
@@ -679,28 +1352,92 @@ function BlockToolbar({
679
1352
  ),
680
1353
  [backgroundImagePool, currentBackground]
681
1354
  );
1355
+ const backgroundCycleImagePool = useMemo(
1356
+ () => {
1357
+ if (currentBackground.kind === "supported" && currentBackground.override.kind === "image") {
1358
+ const currentOverride = currentBackground.override;
1359
+ const currentExistsInPool = backgroundImagePool.some(
1360
+ (item) => item.mediaId === currentOverride.mediaId
1361
+ );
1362
+ if (!currentExistsInPool) {
1363
+ return rankBackgroundImagePoolItems(
1364
+ dedupeBackgroundImagePool([
1365
+ ...imagePoolForOverride(currentOverride),
1366
+ ...backgroundImagePool
1367
+ ])
1368
+ );
1369
+ }
1370
+ }
1371
+ return rankBackgroundImagePoolItems(backgroundImagePool);
1372
+ },
1373
+ [backgroundImagePool, currentBackground]
1374
+ );
682
1375
  const backgroundCapability = useMemo(
683
1376
  () => designBlock ? resolveBackgroundCapability({
684
1377
  block: designBlock,
685
- imagePool: mergedBackgroundImagePool
1378
+ imagePool: mergedBackgroundImagePool,
1379
+ palette: backgroundColorPalette
686
1380
  }) : null,
687
- [designBlock, mergedBackgroundImagePool]
1381
+ [backgroundColorPalette, designBlock, mergedBackgroundImagePool]
1382
+ );
1383
+ const backgroundTransitionCapability = useMemo(
1384
+ () => backgroundCapability ? { ...backgroundCapability, imagePool: backgroundCycleImagePool } : null,
1385
+ [backgroundCapability, backgroundCycleImagePool]
688
1386
  );
689
1387
  const enabledBackgroundModeList = useMemo(
690
1388
  () => backgroundCapability ? enabledBackgroundModes(backgroundCapability) : [],
691
1389
  [backgroundCapability]
692
1390
  );
693
1391
  const hasBackgroundControls = currentBackground.kind === "supported" && enabledBackgroundModeList.length > 1;
694
- const hasCustomizeControls = hasLayoutControls || hasLookControls || hasFacetControls || hasBackgroundControls;
695
- const isSavingCustomize = isHistoryPending;
1392
+ const builtInCustomizeRows = useMemo(
1393
+ () => buildBlockCustomizeBuiltinRows({
1394
+ designBlock,
1395
+ facetControls,
1396
+ hasLayoutControls,
1397
+ hasLookControls,
1398
+ hasBackgroundControls
1399
+ }),
1400
+ [
1401
+ designBlock,
1402
+ facetControls,
1403
+ hasBackgroundControls,
1404
+ hasLayoutControls,
1405
+ hasLookControls
1406
+ ]
1407
+ );
1408
+ const builtInCustomizeRowCount = blockCustomizeBuiltinRowBudget(builtInCustomizeRows);
1409
+ const manifestCustomize = useBlockCustomizeControls({
1410
+ blockId: editorBlockId,
1411
+ blockKind,
1412
+ content: customizeContent,
1413
+ uiStore,
1414
+ theme,
1415
+ maxQuickFields: MAX_QUICK_MANIFEST_FIELDS,
1416
+ maxPopoverRows: MAX_CUSTOMIZE_POPOVER_ROWS,
1417
+ builtInPopoverRows: builtInCustomizeRowCount
1418
+ });
1419
+ const quickManifestFields = manifestCustomize.quickFields;
1420
+ const hasSidebarCustomizeControls = manifestCustomize.hasControls;
1421
+ const hasOverflowCustomizeControls = manifestCustomize.hasOverflowControls;
1422
+ const quickManifestBackend = manifestCustomize.backend;
1423
+ const canCycleBackgroundMode = enabledBackgroundModeList.length > 1;
1424
+ const canCycleBackgroundValue = currentBackground.kind === "supported" && backgroundTransitionCapability ? canCycleBackgroundOverrideValue(
1425
+ currentBackground.override,
1426
+ backgroundTransitionCapability
1427
+ ) : false;
1428
+ const hasCustomizeControls = hasLayoutControls || hasLookControls || hasFacetControls || hasBackgroundControls || quickManifestFields.length > 0 || hasSidebarCustomizeControls;
696
1429
  useEffect(() => {
697
1430
  if (!hasCustomizeControls) {
698
- setCustomizeOpen(false);
1431
+ popover.close();
699
1432
  setBackgroundChooserOpen(false);
700
1433
  }
701
- }, [hasCustomizeControls]);
1434
+ }, [hasCustomizeControls, popover.close]);
702
1435
  useEffect(() => {
703
- if (!customizeOpen || !backgroundCapability?.modes.includes("image")) {
1436
+ if (!popover.open) return;
1437
+ setBackgroundColorPalette(readBackgroundColorPaletteSnapshot());
1438
+ }, [pageDesignState, popover.open]);
1439
+ useEffect(() => {
1440
+ if (!popover.open || !backgroundCapability?.modes.includes("image")) {
704
1441
  return;
705
1442
  }
706
1443
  if (backgroundImagePoolStatus !== "idle") return;
@@ -708,8 +1445,8 @@ function BlockToolbar({
708
1445
  }, [
709
1446
  backgroundCapability,
710
1447
  backgroundImagePoolStatus,
711
- customizeOpen,
712
- loadBackgroundImagePool
1448
+ loadBackgroundImagePool,
1449
+ popover.open
713
1450
  ]);
714
1451
  const handleEdit = (e) => {
715
1452
  e.preventDefault();
@@ -719,36 +1456,40 @@ function BlockToolbar({
719
1456
  state.setPreviewSidebarTab("editor");
720
1457
  state.setDrawerOpen(true);
721
1458
  state.requestFocus({
722
- blockId: unsafeAsEditorBlockId(blockId),
1459
+ blockId: editorBlockId,
723
1460
  fieldPath: focusTarget.fieldPath,
724
1461
  itemIndex: focusTarget.itemIndex
725
1462
  });
726
1463
  };
727
- const cycleLayout = (direction) => (e) => {
728
- e.preventDefault();
729
- e.stopPropagation();
1464
+ const inspectBlock = useCallback(() => {
1465
+ customizeSelection?.inspectBlock(unsafeAsEditorBlockId(blockId));
1466
+ }, [blockId, customizeSelection]);
1467
+ const openSidebarCustomize = useCallback(
1468
+ (e) => {
1469
+ e.preventDefault();
1470
+ e.stopPropagation();
1471
+ inspectBlock();
1472
+ const state = uiStore.getState();
1473
+ setPreviewSidebarTab("editor");
1474
+ state.setPreviewSidebarTab("editor");
1475
+ state.setDrawerOpen(true);
1476
+ },
1477
+ [inspectBlock, uiStore]
1478
+ );
1479
+ const cycleLayout = (direction) => {
730
1480
  if (!designBlock) return;
731
1481
  const action = cyclePageDesignBlockLayout(designBlock, direction);
732
1482
  if (!action) return;
733
1483
  saveBlockCustomizeAction(action);
734
1484
  };
735
- const cycleLook = (direction) => (e) => {
736
- e.preventDefault();
737
- e.stopPropagation();
1485
+ const cycleLook = (direction) => {
738
1486
  if (!designBlock) return;
739
1487
  const action = cyclePageDesignBlockLook(designBlock, direction);
740
1488
  if (!action) return;
741
1489
  saveBlockCustomizeAction(action);
742
1490
  };
743
- const handleFacetCycle = (e) => {
744
- e.preventDefault();
745
- e.stopPropagation();
1491
+ const cycleFacet = (control, direction) => {
746
1492
  if (!designBlock) return;
747
- const facetId = e.currentTarget.dataset.facetId;
748
- const direction = e.currentTarget.dataset.direction;
749
- if (direction !== "prev" && direction !== "next") return;
750
- const control = facetControls.find((candidate) => candidate.facetId === facetId) ?? null;
751
- if (!control) return;
752
1493
  const action = cyclePageDesignBlockFacet({
753
1494
  block: designBlock,
754
1495
  facetId: control.facetId,
@@ -768,7 +1509,7 @@ function BlockToolbar({
768
1509
  const action = setPageDesignBlockBackground({
769
1510
  block: designBlock,
770
1511
  override,
771
- currentContent: content,
1512
+ currentContent: customizeContent,
772
1513
  imagePool: mergedBackgroundImagePool
773
1514
  });
774
1515
  if (!action) return;
@@ -776,304 +1517,117 @@ function BlockToolbar({
776
1517
  setBackgroundChooserOpen(false);
777
1518
  };
778
1519
  const saveBackgroundTransition = (transition) => {
779
- if (!designBlock || !backgroundCapability || currentBackground.kind !== "supported") {
1520
+ if (!designBlock || !backgroundTransitionCapability || currentBackground.kind !== "supported") {
780
1521
  return;
781
1522
  }
782
1523
  const plan = planBackgroundTransition(
783
1524
  currentBackground.override,
784
- backgroundCapability,
1525
+ backgroundTransitionCapability,
785
1526
  transition
786
1527
  );
787
1528
  if (plan.kind !== "apply") return;
788
1529
  const action = setPageDesignBlockBackground({
789
1530
  block: designBlock,
790
1531
  override: plan.override,
791
- currentContent: content,
1532
+ currentContent: customizeContent,
792
1533
  imagePool: mergedBackgroundImagePool
793
1534
  });
794
1535
  if (!action) return;
795
1536
  saveBlockCustomizeAction(action);
796
- };
797
- const saveBlockCustomizeAction = (action) => {
798
- void applyPageDesignAction(action);
799
- };
800
- const toggleCustomize = (e) => {
801
- e.preventDefault();
802
- e.stopPropagation();
803
- if (!customizeOpen) {
804
- setCustomizeReturnFocusTarget(
805
- e.currentTarget instanceof HTMLElement ? e.currentTarget : customizeButtonRef.current
806
- );
807
- }
808
- setCustomizeOpen((open) => !open);
809
- };
810
- return /* @__PURE__ */ jsxs(
811
- "div",
812
- {
813
- className: "pointer-events-auto absolute right-2 z-50 flex gap-1 opacity-0 transition group-focus-within:opacity-100 group-hover:opacity-100",
814
- style: { top: headerClearancePx > 0 ? headerClearancePx + 8 : 8 },
815
- children: [
816
- hasCustomizeControls ? /* @__PURE__ */ jsx(
817
- "button",
818
- {
819
- ref: customizeButtonRef,
820
- type: "button",
821
- onClick: toggleCustomize,
822
- className: previewCustomizePillButtonClassName,
823
- "aria-label": "Customize",
824
- "aria-expanded": customizeOpen,
825
- "aria-haspopup": "dialog",
826
- "aria-controls": customizeOpen ? customizePanelId : void 0,
827
- children: isSavingCustomize ? "Saving..." : "Customize"
828
- }
829
- ) : null,
830
- /* @__PURE__ */ jsx(
831
- "button",
832
- {
833
- type: "button",
834
- onClick: handleEdit,
835
- className: previewCustomizePillButtonClassName,
836
- children: "Edit"
837
- }
838
- ),
839
- customizeOpen && designBlock ? /* @__PURE__ */ jsxs(
840
- "div",
841
- {
842
- id: customizePanelId,
843
- className: "builder-dashboard-chrome-theme fixed inset-x-3 bottom-[calc(var(--builder-editor-chrome-offset,0px)+1rem)] z-[70] rounded-lg border bg-background p-3 text-foreground shadow-xl sm:absolute sm:inset-x-auto sm:bottom-auto sm:right-0 sm:top-9 sm:w-72",
844
- role: "dialog",
845
- "aria-modal": "true",
846
- "aria-label": "Customize section",
847
- "aria-describedby": customizeDescriptionId,
848
- ref: customizeDialogRef,
849
- onKeyDown: handleCustomizeDialogKeyDown,
850
- onClick: (event) => event.stopPropagation(),
851
- children: [
852
- /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
853
- /* @__PURE__ */ jsxs("div", { children: [
854
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: "Section" }),
855
- /* @__PURE__ */ jsx(
856
- "div",
857
- {
858
- id: customizeDescriptionId,
859
- className: "text-sm font-semibold",
860
- children: formatBlockKindLabel(blockKind)
861
- }
862
- )
863
- ] }),
864
- /* @__PURE__ */ jsx(
865
- "button",
866
- {
867
- ref: registerCustomizeInitialFocus,
868
- type: "button",
869
- className: "rounded-full px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
870
- onClick: (event) => {
871
- event.preventDefault();
872
- event.stopPropagation();
873
- closeCustomize();
874
- },
875
- children: "Close"
876
- }
877
- )
878
- ] }),
879
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
880
- designBlock.layout && hasLayoutControls ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
881
- /* @__PURE__ */ jsxs("div", { children: [
882
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Layout" }),
883
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: layoutLabel(
884
- designBlock.availableLayouts,
885
- designBlock.layout.effectiveValue
886
- ) })
887
- ] }),
888
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
889
- /* @__PURE__ */ jsx(
890
- "button",
891
- {
892
- type: "button",
893
- onClick: cycleLayout("prev"),
894
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
895
- "aria-label": "Previous layout",
896
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
897
- }
898
- ),
899
- /* @__PURE__ */ jsx(
900
- "button",
901
- {
902
- type: "button",
903
- onClick: cycleLayout("next"),
904
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
905
- "aria-label": "Next layout",
906
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
907
- }
908
- )
909
- ] })
910
- ] }) : null,
911
- designBlock.layout?.status === "unsupported" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-amber-700", children: "This layout is no longer available. The preview is using the closest safe option." }) : null,
912
- facetControls.map((control) => /* @__PURE__ */ jsxs(
913
- "div",
914
- {
915
- className: "flex items-center justify-between gap-2",
916
- children: [
917
- /* @__PURE__ */ jsxs("div", { children: [
918
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: control.label }),
919
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: control.valueLabel })
920
- ] }),
921
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
922
- /* @__PURE__ */ jsx(
923
- "button",
924
- {
925
- type: "button",
926
- onClick: handleFacetCycle,
927
- "data-facet-id": control.facetId,
928
- "data-direction": "prev",
929
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
930
- "aria-label": `Previous ${control.label.toLowerCase()}`,
931
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
932
- }
933
- ),
934
- /* @__PURE__ */ jsx(
935
- "button",
936
- {
937
- type: "button",
938
- onClick: handleFacetCycle,
939
- "data-facet-id": control.facetId,
940
- "data-direction": "next",
941
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
942
- "aria-label": `Next ${control.label.toLowerCase()}`,
943
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
944
- }
945
- )
946
- ] })
947
- ]
948
- },
949
- control.facetId
950
- )),
951
- hasBackgroundControls && currentBackground.kind === "supported" && backgroundCapability ? /* @__PURE__ */ jsxs("div", { children: [
952
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
953
- /* @__PURE__ */ jsxs(
954
- "button",
955
- {
956
- type: "button",
957
- className: "flex min-w-0 items-center gap-2 text-left",
958
- "aria-expanded": backgroundChooserOpen,
959
- onClick: (event) => {
960
- event.preventDefault();
961
- event.stopPropagation();
962
- setBackgroundChooserOpen((open) => !open);
963
- },
964
- children: [
965
- /* @__PURE__ */ jsx(BackgroundPreviewSwatch, { override: currentBackground.override }),
966
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
967
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Background" }),
968
- /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-muted-foreground", children: backgroundOverrideLabel(currentBackground.override) })
969
- ] })
970
- ]
971
- }
972
- ),
973
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
974
- /* @__PURE__ */ jsxs("div", { className: "flex items-center overflow-hidden rounded-full border bg-background", children: [
975
- /* @__PURE__ */ jsx(
976
- "button",
977
- {
978
- type: "button",
979
- onClick: handleBackgroundCycle({
980
- kind: "cycle-mode",
981
- direction: "prev"
982
- }),
983
- className: "grid h-8 w-8 place-items-center hover:bg-muted",
984
- "aria-label": "Previous background mode",
985
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
986
- }
987
- ),
988
- /* @__PURE__ */ jsx(
989
- "button",
990
- {
991
- type: "button",
992
- onClick: handleBackgroundCycle({
993
- kind: "cycle-mode",
994
- direction: "next"
995
- }),
996
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted",
997
- "aria-label": "Next background mode",
998
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
999
- }
1000
- )
1001
- ] }),
1002
- /* @__PURE__ */ jsxs("div", { className: "flex items-center overflow-hidden rounded-full border bg-background", children: [
1003
- /* @__PURE__ */ jsx(
1004
- "button",
1005
- {
1006
- type: "button",
1007
- onClick: handleBackgroundCycle({
1008
- kind: "cycle-value",
1009
- direction: "prev"
1010
- }),
1011
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1012
- "aria-label": "Previous background value",
1013
- disabled: currentBackground.override.kind === "default",
1014
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
1015
- }
1016
- ),
1017
- /* @__PURE__ */ jsx(
1018
- "button",
1019
- {
1020
- type: "button",
1021
- onClick: handleBackgroundCycle({
1022
- kind: "cycle-value",
1023
- direction: "next"
1024
- }),
1025
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1026
- "aria-label": "Next background value",
1027
- disabled: currentBackground.override.kind === "default",
1028
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
1029
- }
1030
- )
1031
- ] })
1032
- ] })
1033
- ] }),
1034
- backgroundChooserOpen ? /* @__PURE__ */ jsx(
1035
- BackgroundChooser,
1036
- {
1037
- capability: backgroundCapability,
1038
- current: currentBackground.override,
1039
- enabledModes: enabledBackgroundModeList,
1040
- imagePoolStatus: backgroundImagePoolStatus,
1041
- onChoose: chooseBackgroundOverride
1042
- }
1043
- ) : null
1044
- ] }) : null,
1045
- hasLookControls ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1046
- /* @__PURE__ */ jsxs("div", { children: [
1047
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Look" }),
1048
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: lookLabel(designBlock) })
1049
- ] }),
1050
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
1051
- /* @__PURE__ */ jsx(
1052
- "button",
1053
- {
1054
- type: "button",
1055
- onClick: cycleLook("prev"),
1056
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1057
- "aria-label": "Previous look",
1058
- disabled: !canCycleLook,
1059
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
1060
- }
1061
- ),
1062
- /* @__PURE__ */ jsx(
1063
- "button",
1064
- {
1065
- type: "button",
1066
- onClick: cycleLook("next"),
1067
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1068
- "aria-label": "Next look",
1069
- disabled: !canCycleLook,
1070
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
1071
- }
1072
- )
1073
- ] })
1074
- ] }) : null,
1075
- historyFailure ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-700", children: historyFailure }) : null
1076
- ] })
1537
+ };
1538
+ const saveBlockCustomizeAction = (action) => {
1539
+ void applyPageDesignAction(action, { runtimeContent: customizeContent });
1540
+ };
1541
+ const toggleCustomize = (e) => {
1542
+ if (!popover.open) {
1543
+ inspectBlock();
1544
+ }
1545
+ popover.toggle(e);
1546
+ };
1547
+ return /* @__PURE__ */ jsxs(
1548
+ PreviewChromeFloatingControl,
1549
+ {
1550
+ anchorSelector: "[data-block-id]",
1551
+ placement,
1552
+ fallbackClassName: "pointer-events-auto absolute z-50 flex gap-1 opacity-0 transition group-focus-within:opacity-100 group-hover:opacity-100",
1553
+ fallbackStyle: {
1554
+ top: placement.topPx,
1555
+ right: placement.rightPx
1556
+ },
1557
+ forceVisible: popover.open,
1558
+ children: [
1559
+ hasCustomizeControls ? /* @__PURE__ */ jsx(
1560
+ CustomizePillButton,
1561
+ {
1562
+ buttonRef: popover.buttonRef,
1563
+ ariaLabel: "Customize",
1564
+ open: popover.open,
1565
+ controlsId: popover.panelId,
1566
+ onClick: toggleCustomize
1567
+ }
1568
+ ) : null,
1569
+ /* @__PURE__ */ jsx(
1570
+ "button",
1571
+ {
1572
+ type: "button",
1573
+ onClick: handleEdit,
1574
+ className: previewCustomizePillButtonClassName,
1575
+ children: "Edit"
1576
+ }
1577
+ ),
1578
+ popover.open && (designBlock || hasSidebarCustomizeControls) ? /* @__PURE__ */ jsxs(
1579
+ CustomizePopoverShell,
1580
+ {
1581
+ panelId: popover.panelId,
1582
+ descriptionId: popover.descriptionId,
1583
+ dialogLabel: "Customize section",
1584
+ eyebrow: "Section",
1585
+ title: formatBlockKindLabel(blockKind),
1586
+ dialogRef: popover.dialogRef,
1587
+ closeButtonRef: popover.registerInitialFocus,
1588
+ onClose: popover.close,
1589
+ onKeyDown: popover.handleDialogKeyDown,
1590
+ spacing: "compact",
1591
+ children: [
1592
+ builtInCustomizeRows.map(
1593
+ (row) => renderBlockCustomizeBuiltinRow(row, {
1594
+ backgroundCapability,
1595
+ backgroundChooserOpen,
1596
+ canCycleBackgroundMode,
1597
+ canCycleBackgroundValue,
1598
+ currentBackground,
1599
+ cycleLayout,
1600
+ cycleLook,
1601
+ cycleFacet,
1602
+ enabledBackgroundModeList,
1603
+ handleBackgroundCycle,
1604
+ imagePoolStatus: backgroundImagePoolStatus,
1605
+ mergedBackgroundImagePool,
1606
+ onChooseBackgroundOverride: chooseBackgroundOverride,
1607
+ onToggleBackgroundChooser: () => setBackgroundChooserOpen((open) => !open)
1608
+ })
1609
+ ),
1610
+ quickManifestFields.length > 0 && quickManifestBackend ? /* @__PURE__ */ jsx("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsx(
1611
+ FormRenderer,
1612
+ {
1613
+ fields: quickManifestFields,
1614
+ backend: quickManifestBackend,
1615
+ showSettingsTrigger: false,
1616
+ blockKind,
1617
+ className: "space-y-3"
1618
+ }
1619
+ ) }) : null,
1620
+ historyFailure ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-700", children: historyFailure }) : null,
1621
+ hasOverflowCustomizeControls ? /* @__PURE__ */ jsx("div", { className: "border-t pt-2", children: /* @__PURE__ */ jsx(
1622
+ "button",
1623
+ {
1624
+ type: "button",
1625
+ "aria-label": "Open detailed customization settings",
1626
+ className: "w-full rounded-md border bg-background px-3 py-2 text-sm font-medium hover:bg-muted",
1627
+ onClick: openSidebarCustomize,
1628
+ children: "Customize"
1629
+ }
1630
+ ) }) : null
1077
1631
  ]
1078
1632
  }
1079
1633
  ) : null
@@ -1081,16 +1635,178 @@ function BlockToolbar({
1081
1635
  }
1082
1636
  );
1083
1637
  }
1638
+ function renderBlockCustomizeBuiltinRow(row, context) {
1639
+ switch (row.kind) {
1640
+ case "variant":
1641
+ return /* @__PURE__ */ jsx(
1642
+ CustomizeCycleRow,
1643
+ {
1644
+ label: "Variant",
1645
+ valueLabel: layoutLabel(
1646
+ row.block.availableLayouts,
1647
+ row.block.layout?.effectiveValue ?? ""
1648
+ ),
1649
+ previousLabel: "Previous variant",
1650
+ nextLabel: "Next variant",
1651
+ availability: cycleAvailability({ kind: "available" }),
1652
+ onPrevious: () => context.cycleLayout("prev"),
1653
+ onNext: () => context.cycleLayout("next")
1654
+ },
1655
+ "variant"
1656
+ );
1657
+ case "unsupported-variant-warning":
1658
+ return /* @__PURE__ */ jsx("p", { className: "text-xs text-amber-700", children: "This layout is no longer available. The preview is using the closest safe option." }, "unsupported-variant-warning");
1659
+ case "look":
1660
+ return /* @__PURE__ */ jsx(
1661
+ CustomizeCycleRow,
1662
+ {
1663
+ label: "Look",
1664
+ valueLabel: lookLabel(row.block),
1665
+ previousLabel: "Previous look",
1666
+ nextLabel: "Next look",
1667
+ availability: cycleAvailability(
1668
+ canCycleLookForRow(row.block) ? { kind: "available" } : { kind: "single-option" }
1669
+ ),
1670
+ onPrevious: () => context.cycleLook("prev"),
1671
+ onNext: () => context.cycleLook("next")
1672
+ },
1673
+ "look"
1674
+ );
1675
+ case "background":
1676
+ if (context.currentBackground.kind !== "supported" || !context.backgroundCapability) {
1677
+ return null;
1678
+ }
1679
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1680
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch overflow-hidden rounded-full border bg-background", children: [
1681
+ /* @__PURE__ */ jsx(
1682
+ "button",
1683
+ {
1684
+ type: "button",
1685
+ onClick: context.handleBackgroundCycle({
1686
+ kind: "cycle-mode",
1687
+ direction: "prev"
1688
+ }),
1689
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1690
+ "aria-label": "Previous background mode",
1691
+ disabled: !context.canCycleBackgroundMode,
1692
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
1693
+ }
1694
+ ),
1695
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 border-x px-2 py-1 text-center", children: [
1696
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium leading-3 text-muted-foreground", children: "Background:" }),
1697
+ /* @__PURE__ */ jsx("div", { className: "truncate text-xs font-semibold leading-4", children: backgroundModeLabel(context.currentBackground.override.kind) })
1698
+ ] }),
1699
+ /* @__PURE__ */ jsx(
1700
+ "button",
1701
+ {
1702
+ type: "button",
1703
+ onClick: context.handleBackgroundCycle({
1704
+ kind: "cycle-mode",
1705
+ direction: "next"
1706
+ }),
1707
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1708
+ "aria-label": "Next background mode",
1709
+ disabled: !context.canCycleBackgroundMode,
1710
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
1711
+ }
1712
+ )
1713
+ ] }),
1714
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch overflow-hidden rounded-full border bg-background", children: [
1715
+ /* @__PURE__ */ jsx(
1716
+ "button",
1717
+ {
1718
+ type: "button",
1719
+ onClick: context.handleBackgroundCycle({
1720
+ kind: "cycle-value",
1721
+ direction: "prev"
1722
+ }),
1723
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1724
+ "aria-label": "Previous background value",
1725
+ disabled: !context.canCycleBackgroundValue,
1726
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
1727
+ }
1728
+ ),
1729
+ /* @__PURE__ */ jsxs(
1730
+ "button",
1731
+ {
1732
+ type: "button",
1733
+ className: "flex min-w-0 flex-1 items-center justify-center gap-2 border-x px-2 py-1 text-center hover:bg-muted",
1734
+ "aria-expanded": context.backgroundChooserOpen,
1735
+ "aria-label": `Choose background ${backgroundOverrideLabel(context.currentBackground.override).toLowerCase()}`,
1736
+ onClick: (event) => {
1737
+ event.preventDefault();
1738
+ event.stopPropagation();
1739
+ context.onToggleBackgroundChooser();
1740
+ },
1741
+ children: [
1742
+ /* @__PURE__ */ jsx(
1743
+ BackgroundPreviewSwatch,
1744
+ {
1745
+ override: context.currentBackground.override,
1746
+ imagePool: context.mergedBackgroundImagePool
1747
+ }
1748
+ ),
1749
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs font-medium", children: backgroundOverrideValueLabel(
1750
+ context.currentBackground.override,
1751
+ context.mergedBackgroundImagePool
1752
+ ) })
1753
+ ]
1754
+ }
1755
+ ),
1756
+ /* @__PURE__ */ jsx(
1757
+ "button",
1758
+ {
1759
+ type: "button",
1760
+ onClick: context.handleBackgroundCycle({
1761
+ kind: "cycle-value",
1762
+ direction: "next"
1763
+ }),
1764
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
1765
+ "aria-label": "Next background value",
1766
+ disabled: !context.canCycleBackgroundValue,
1767
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
1768
+ }
1769
+ )
1770
+ ] }),
1771
+ context.backgroundChooserOpen ? /* @__PURE__ */ jsx(
1772
+ BackgroundChooser,
1773
+ {
1774
+ capability: context.backgroundCapability,
1775
+ current: context.currentBackground.override,
1776
+ imagePoolStatus: context.imagePoolStatus,
1777
+ onChoose: context.onChooseBackgroundOverride
1778
+ }
1779
+ ) : null
1780
+ ] }, "background");
1781
+ case "facet":
1782
+ return /* @__PURE__ */ jsx(
1783
+ CustomizeCycleRow,
1784
+ {
1785
+ label: row.control.label,
1786
+ valueLabel: row.control.valueLabel,
1787
+ previousLabel: `Previous ${row.control.label.toLowerCase()}`,
1788
+ nextLabel: `Next ${row.control.label.toLowerCase()}`,
1789
+ availability: cycleAvailability({ kind: "available" }),
1790
+ onPrevious: () => context.cycleFacet(row.control, "prev"),
1791
+ onNext: () => context.cycleFacet(row.control, "next")
1792
+ },
1793
+ `facet:${row.control.facetId}`
1794
+ );
1795
+ default:
1796
+ return assertNever2(row, "Unhandled block customize builtin row");
1797
+ }
1798
+ }
1799
+ function canCycleLookForRow(block) {
1800
+ return block.availableLooks.length > 1;
1801
+ }
1084
1802
  function BackgroundChooser({
1085
1803
  capability,
1086
1804
  current,
1087
- enabledModes,
1088
1805
  imagePoolStatus,
1089
1806
  onChoose
1090
1807
  }) {
1091
1808
  const mediaEditorContext = useMediaEditorOptional();
1092
1809
  const [mediaPickerOpenKey, setMediaPickerOpenKey] = useState(0);
1093
- const enabledModeSet = new Set(enabledModes);
1094
1810
  const images = rankBackgroundImagePoolItems(capability.imagePool ?? []);
1095
1811
  const recommendedTokens = capability.recommendedColorTokens ?? capability.colorTokens ?? [];
1096
1812
  const advancedTokens = capability.advancedColorTokens ?? [];
@@ -1106,21 +1822,30 @@ function BackgroundChooser({
1106
1822
  onChoose({ kind: "image", mediaId: parsed.value });
1107
1823
  setMediaPickerOpenKey(0);
1108
1824
  };
1109
- return /* @__PURE__ */ jsxs("div", { className: "mt-2 max-h-80 overflow-y-auto rounded-md border bg-muted/20 p-2", children: [
1110
- /* @__PURE__ */ jsx("div", { className: "mb-2 grid grid-cols-4 gap-1", children: ["default", "color", "gradient", "image"].map((mode) => /* @__PURE__ */ jsx(
1825
+ const renderColorOptions = () => recommendedTokens.length || advancedTokens.length ? /* @__PURE__ */ jsxs("div", { children: [
1826
+ /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Colors" }),
1827
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", children: recommendedTokens.map((token) => /* @__PURE__ */ jsxs(
1111
1828
  "button",
1112
1829
  {
1113
1830
  type: "button",
1114
- className: `rounded-md border px-2 py-1 text-xs ${current.kind === mode ? "border-foreground bg-foreground text-background" : "bg-background hover:bg-muted"} disabled:cursor-not-allowed disabled:opacity-50`,
1115
- disabled: !enabledModeSet.has(mode),
1116
- onClick: chooseFromClick(defaultBackgroundOverrideForMode(mode, capability)),
1117
- children: backgroundModeLabel(mode)
1831
+ className: `flex min-w-0 items-center gap-2 rounded-md border px-2 py-1 text-left text-xs hover:bg-muted ${current.kind === "color" && current.token === token ? "border-foreground" : "bg-background"}`,
1832
+ onClick: chooseFromClick({ kind: "color", token }),
1833
+ children: [
1834
+ /* @__PURE__ */ jsx(
1835
+ "span",
1836
+ {
1837
+ className: "h-3 w-3 shrink-0 rounded-full border",
1838
+ style: { backgroundColor: tokenSwatchCss(token) }
1839
+ }
1840
+ ),
1841
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: formatTokenLabel(token) })
1842
+ ]
1118
1843
  },
1119
- mode
1844
+ token
1120
1845
  )) }),
1121
- recommendedTokens.length || advancedTokens.length ? /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
1122
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Colors" }),
1123
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", children: recommendedTokens.map((token) => /* @__PURE__ */ jsxs(
1846
+ advancedTokens.length ? /* @__PURE__ */ jsxs("details", { className: "mt-1", children: [
1847
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: "More colors" }),
1848
+ /* @__PURE__ */ jsx("div", { className: "mt-1 grid grid-cols-2 gap-1", children: advancedTokens.map((token) => /* @__PURE__ */ jsxs(
1124
1849
  "button",
1125
1850
  {
1126
1851
  type: "button",
@@ -1138,78 +1863,72 @@ function BackgroundChooser({
1138
1863
  ]
1139
1864
  },
1140
1865
  token
1141
- )) }),
1142
- advancedTokens.length ? /* @__PURE__ */ jsxs("details", { className: "mt-1", children: [
1143
- /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: "More colors" }),
1144
- /* @__PURE__ */ jsx("div", { className: "mt-1 grid grid-cols-2 gap-1", children: advancedTokens.map((token) => /* @__PURE__ */ jsxs(
1145
- "button",
1146
- {
1147
- type: "button",
1148
- className: `flex min-w-0 items-center gap-2 rounded-md border px-2 py-1 text-left text-xs hover:bg-muted ${current.kind === "color" && current.token === token ? "border-foreground" : "bg-background"}`,
1149
- onClick: chooseFromClick({ kind: "color", token }),
1150
- children: [
1151
- /* @__PURE__ */ jsx(
1152
- "span",
1153
- {
1154
- className: "h-3 w-3 shrink-0 rounded-full border",
1155
- style: { backgroundColor: tokenSwatchCss(token) }
1156
- }
1157
- ),
1158
- /* @__PURE__ */ jsx("span", { className: "truncate", children: formatTokenLabel(token) })
1159
- ]
1160
- },
1161
- token
1162
- )) })
1163
- ] }) : null
1164
- ] }) : null,
1165
- capability.gradientIds?.length ? /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
1166
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Gradients" }),
1167
- /* @__PURE__ */ jsx("div", { className: "space-y-1", children: capability.gradientIds.map((gradientId) => /* @__PURE__ */ jsx(
1168
- "button",
1169
- {
1170
- type: "button",
1171
- className: `w-full rounded-md border px-2 py-1 text-left text-xs hover:bg-muted ${current.kind === "gradient" && current.gradientId === gradientId ? "border-foreground" : "bg-background"}`,
1172
- onClick: chooseFromClick({ kind: "gradient", gradientId }),
1173
- children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
1174
- /* @__PURE__ */ jsx(
1175
- "span",
1176
- {
1177
- className: "h-4 w-8 shrink-0 rounded border",
1178
- style: { backgroundImage: gradientPreviewCss(gradientId) }
1179
- }
1180
- ),
1181
- /* @__PURE__ */ jsx("span", { className: "truncate", children: gradientLabel(gradientId) })
1182
- ] })
1183
- },
1184
- gradientId
1185
1866
  )) })
1186
- ] }) : null,
1187
- capability.modes.includes("image") ? /* @__PURE__ */ jsxs("div", { children: [
1188
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Images" }),
1189
- mediaEditorContext ? /* @__PURE__ */ jsxs(
1190
- "button",
1191
- {
1192
- type: "button",
1193
- className: "mb-2 inline-flex w-full items-center justify-center gap-2 rounded-md border bg-background px-2 py-1.5 text-xs font-medium hover:bg-muted",
1194
- onClick: (event) => {
1195
- event.preventDefault();
1196
- event.stopPropagation();
1197
- setMediaPickerOpenKey((key) => key + 1);
1198
- },
1199
- children: [
1200
- /* @__PURE__ */ jsx(Upload, { size: 13 }),
1201
- "Media library"
1202
- ]
1203
- }
1204
- ) : null,
1205
- images.length ? /* @__PURE__ */ jsx("div", { className: "space-y-1", children: images.map((item) => /* @__PURE__ */ jsx(
1867
+ ] }) : null
1868
+ ] }) : null;
1869
+ const renderGradientOptions = () => capability.gradientIds?.length ? /* @__PURE__ */ jsxs("div", { children: [
1870
+ /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Gradients" }),
1871
+ /* @__PURE__ */ jsx("div", { className: "space-y-1", children: capability.gradientIds.map((gradientId) => /* @__PURE__ */ jsx(
1872
+ "button",
1873
+ {
1874
+ type: "button",
1875
+ className: `w-full rounded-md border px-2 py-1 text-left text-xs hover:bg-muted ${current.kind === "gradient" && current.gradientId === gradientId ? "border-foreground" : "bg-background"}`,
1876
+ onClick: chooseFromClick({ kind: "gradient", gradientId }),
1877
+ children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
1878
+ /* @__PURE__ */ jsx(
1879
+ "span",
1880
+ {
1881
+ className: "h-4 w-8 shrink-0 rounded border",
1882
+ style: { backgroundImage: gradientPreviewCss(gradientId) }
1883
+ }
1884
+ ),
1885
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: gradientLabel(gradientId) })
1886
+ ] })
1887
+ },
1888
+ gradientId
1889
+ )) })
1890
+ ] }) : null;
1891
+ const renderImageOptions = () => capability.modes.includes("image") ? /* @__PURE__ */ jsxs("div", { children: [
1892
+ /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Images" }),
1893
+ mediaEditorContext ? /* @__PURE__ */ jsxs(
1894
+ "button",
1895
+ {
1896
+ type: "button",
1897
+ className: "mb-2 inline-flex w-full items-center justify-center gap-2 rounded-md border bg-background px-2 py-1.5 text-xs font-medium hover:bg-muted",
1898
+ onClick: (event) => {
1899
+ event.preventDefault();
1900
+ event.stopPropagation();
1901
+ setMediaPickerOpenKey((key) => key + 1);
1902
+ },
1903
+ children: [
1904
+ /* @__PURE__ */ jsx(Upload, { size: 13 }),
1905
+ "Media library"
1906
+ ]
1907
+ }
1908
+ ) : null,
1909
+ images.length ? /* @__PURE__ */ jsx("div", { className: "space-y-1", children: images.map((item) => {
1910
+ const thumbnailUrl = backgroundImagePreviewUrl(
1911
+ item,
1912
+ mediaEditorContext
1913
+ );
1914
+ return /* @__PURE__ */ jsx(
1206
1915
  "button",
1207
1916
  {
1208
1917
  type: "button",
1209
1918
  className: `w-full rounded-md border px-2 py-1 text-left text-xs hover:bg-muted ${current.kind === "image" && current.mediaId === item.mediaId ? "border-foreground" : "bg-background"}`,
1210
- onClick: chooseFromClick({ kind: "image", mediaId: item.mediaId }),
1919
+ onClick: chooseFromClick({
1920
+ kind: "image",
1921
+ mediaId: item.mediaId
1922
+ }),
1211
1923
  children: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
1212
- /* @__PURE__ */ jsx("span", { className: "grid h-7 w-9 shrink-0 place-items-center rounded border bg-muted", children: /* @__PURE__ */ jsx(Image, { size: 14 }) }),
1924
+ /* @__PURE__ */ jsx("span", { className: "grid h-7 w-9 shrink-0 place-items-center overflow-hidden rounded border bg-muted", children: thumbnailUrl ? /* @__PURE__ */ jsx(
1925
+ "img",
1926
+ {
1927
+ alt: "",
1928
+ className: "h-full w-full object-cover",
1929
+ src: thumbnailUrl
1930
+ }
1931
+ ) : /* @__PURE__ */ jsx(Image, { size: 14 }) }),
1213
1932
  /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
1214
1933
  /* @__PURE__ */ jsx("span", { className: "block truncate", children: item.label ?? "Image" }),
1215
1934
  /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: imageSourceLabel(item.source) })
@@ -1217,25 +1936,51 @@ function BackgroundChooser({
1217
1936
  ] })
1218
1937
  },
1219
1938
  `${item.source}:${item.mediaId}`
1220
- )) }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: imagePoolStatus === "loading" ? "Loading images..." : "No images available yet." }),
1221
- mediaPickerOpenKey > 0 && mediaEditorContext ? /* @__PURE__ */ jsx("div", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(
1222
- MediaEditor,
1223
- {
1224
- value: void 0,
1225
- onChange: chooseMediaValue,
1226
- allowedKinds: ["image"],
1227
- mediaPickerAutoOpen: true,
1228
- showPreview: false,
1229
- showAltInput: false,
1230
- uploadIntent: { tag: "media-library", allowedRenderUses: ["image"] },
1231
- onUploadComplete: (asset) => {
1232
- onChoose({ kind: "image", mediaId: asset.id });
1233
- setMediaPickerOpenKey(0);
1234
- }
1939
+ );
1940
+ }) }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: imagePoolStatus === "loading" ? "Loading images..." : "No background-ready images yet." }),
1941
+ mediaPickerOpenKey > 0 && mediaEditorContext ? /* @__PURE__ */ jsx("div", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(
1942
+ MediaEditor,
1943
+ {
1944
+ value: void 0,
1945
+ onChange: chooseMediaValue,
1946
+ allowedKinds: ["image"],
1947
+ mediaPickerAutoOpen: true,
1948
+ mediaPickerPolicy: {
1949
+ assetSelection: backgroundMediaPickerAssetSelection
1235
1950
  },
1236
- mediaPickerOpenKey
1237
- ) }) : null
1238
- ] }) : null
1951
+ showPreview: false,
1952
+ showAltInput: false,
1953
+ uploadIntent: {
1954
+ tag: "media-library",
1955
+ allowedRenderUses: ["image"]
1956
+ },
1957
+ onUploadComplete: (asset) => {
1958
+ if (backgroundMediaPickerAssetSelection(asset).tag === "disabled") {
1959
+ return;
1960
+ }
1961
+ onChoose({ kind: "image", mediaId: asset.id });
1962
+ setMediaPickerOpenKey(0);
1963
+ }
1964
+ },
1965
+ mediaPickerOpenKey
1966
+ ) }) : null
1967
+ ] }) : null;
1968
+ return /* @__PURE__ */ jsxs("div", { className: "mt-2 max-h-80 overflow-y-auto rounded-md border bg-muted/20 p-2", children: [
1969
+ current.kind === "default" ? /* @__PURE__ */ jsxs(
1970
+ "button",
1971
+ {
1972
+ type: "button",
1973
+ className: "flex w-full items-center gap-2 rounded-md border border-foreground bg-background px-2 py-1 text-left text-xs",
1974
+ onClick: chooseFromClick(defaultBackgroundOverrideForMode("default", capability)),
1975
+ children: [
1976
+ /* @__PURE__ */ jsx(BackgroundPreviewSwatch, { override: current, imagePool: images }),
1977
+ /* @__PURE__ */ jsx("span", { children: "Default background" })
1978
+ ]
1979
+ }
1980
+ ) : null,
1981
+ current.kind === "color" ? renderColorOptions() : null,
1982
+ current.kind === "gradient" ? renderGradientOptions() : null,
1983
+ current.kind === "image" ? renderImageOptions() : null
1239
1984
  ] });
1240
1985
  }
1241
1986
  function computeDefaultFocus(blockKind, content) {
@@ -1260,7 +2005,7 @@ function findFirstFieldPath(fields, data, prefix) {
1260
2005
  }
1261
2006
  const nextPrefix = [...prefix, field.id];
1262
2007
  if (field.type === "group") {
1263
- const groupValue = isRecord3(data) ? data[field.id] : void 0;
2008
+ const groupValue = isRecord4(data) ? data[field.id] : void 0;
1264
2009
  const nested = findFirstFieldPath(
1265
2010
  field.schema.fields,
1266
2011
  groupValue,
@@ -1270,7 +2015,7 @@ function findFirstFieldPath(fields, data, prefix) {
1270
2015
  continue;
1271
2016
  }
1272
2017
  if (field.type === "repeater" && field.schema) {
1273
- const collection = isRecord3(data) ? data[field.id] : void 0;
2018
+ const collection = isRecord4(data) ? data[field.id] : void 0;
1274
2019
  const items = isUnknownArray(collection) ? collection : [];
1275
2020
  if (items.length > 0) {
1276
2021
  const firstItem = items[0];
@@ -1319,8 +2064,36 @@ function backgroundOverrideLabel(override) {
1319
2064
  return "Image";
1320
2065
  }
1321
2066
  }
1322
- function BackgroundPreviewSwatch({ override }) {
1323
- const baseClassName = "grid h-8 w-10 shrink-0 place-items-center overflow-hidden rounded-md border";
2067
+ function backgroundOverrideValueLabel(override, imagePool) {
2068
+ switch (override.kind) {
2069
+ case "default":
2070
+ return "Default";
2071
+ case "color":
2072
+ return formatTokenLabel(override.token);
2073
+ case "gradient":
2074
+ return gradientLabel(override.gradientId);
2075
+ case "image":
2076
+ return imagePool.find((item) => item.mediaId === override.mediaId)?.label ?? "Image";
2077
+ }
2078
+ }
2079
+ function canCycleBackgroundOverrideValue(override, capability) {
2080
+ switch (override.kind) {
2081
+ case "default":
2082
+ return false;
2083
+ case "color":
2084
+ return (capability.colorTokens?.length ?? 0) > 1;
2085
+ case "gradient":
2086
+ return (capability.gradientIds?.length ?? 0) > 1;
2087
+ case "image":
2088
+ return rankBackgroundImagePoolItems(capability.imagePool ?? []).length > 1;
2089
+ }
2090
+ }
2091
+ function BackgroundPreviewSwatch({
2092
+ override,
2093
+ imagePool
2094
+ }) {
2095
+ const mediaEditorContext = useMediaEditorOptional();
2096
+ const baseClassName = "grid h-6 w-9 shrink-0 place-items-center overflow-hidden rounded-md border";
1324
2097
  switch (override.kind) {
1325
2098
  case "default":
1326
2099
  return /* @__PURE__ */ jsx("span", { className: `${baseClassName} bg-background`, children: /* @__PURE__ */ jsx("span", { className: "h-3 w-3 rounded-full border bg-muted" }) });
@@ -1341,8 +2114,47 @@ function BackgroundPreviewSwatch({ override }) {
1341
2114
  }
1342
2115
  );
1343
2116
  case "image":
1344
- return /* @__PURE__ */ jsx("span", { className: `${baseClassName} bg-muted`, children: /* @__PURE__ */ jsx(Image, { size: 15 }) });
2117
+ const imageItem = imagePool.find((item) => item.mediaId === override.mediaId);
2118
+ const thumbnailUrl = imageItem ? backgroundImagePreviewUrl(imageItem, mediaEditorContext) : void 0;
2119
+ return /* @__PURE__ */ jsx("span", { className: `${baseClassName} bg-muted`, children: thumbnailUrl ? /* @__PURE__ */ jsx("img", { alt: "", className: "h-full w-full object-cover", src: thumbnailUrl }) : /* @__PURE__ */ jsx(Image, { size: 15 }) });
2120
+ }
2121
+ }
2122
+ function backgroundImagePreviewUrl(item, mediaEditorContext) {
2123
+ const asset = item.mediaAsset;
2124
+ if (!asset) return void 0;
2125
+ return resolveImageUrlWithContext(
2126
+ {
2127
+ type: "image",
2128
+ assetId: asset.id,
2129
+ ...asset.identifier ? { identifier: asset.identifier } : {},
2130
+ storageBucket: asset.storageBucket,
2131
+ storagePath: asset.storagePath,
2132
+ ...asset.width != null ? { width: asset.width } : {},
2133
+ ...asset.height != null ? { height: asset.height } : {},
2134
+ mimeType: asset.mimeType
2135
+ },
2136
+ { width: 72, height: 48, resize: "cover", quality: 70 },
2137
+ mediaEditorContext?.imageResolveContext ?? null
2138
+ );
2139
+ }
2140
+ function backgroundMediaPickerAssetSelection(asset) {
2141
+ if (isMarkedBackgroundAsset(asset)) return { tag: "selectable" };
2142
+ if (asset.width == null || asset.height == null) {
2143
+ return {
2144
+ tag: "disabled",
2145
+ reason: "This image is missing dimensions, so it cannot be checked for background use."
2146
+ };
2147
+ }
2148
+ if (asset.width < SECTION_BACKGROUND_MIN_SOURCE_WIDTH || asset.height < SECTION_BACKGROUND_MIN_SOURCE_HEIGHT) {
2149
+ return {
2150
+ tag: "disabled",
2151
+ reason: `Background images need to be at least ${SECTION_BACKGROUND_MIN_SOURCE_WIDTH}x${SECTION_BACKGROUND_MIN_SOURCE_HEIGHT}. This image is ${asset.width}x${asset.height}.`
2152
+ };
1345
2153
  }
2154
+ return { tag: "selectable" };
2155
+ }
2156
+ function isMarkedBackgroundAsset(asset) {
2157
+ return asset.fileUses?.some((fileUse) => fileUse.tag === "site-background-asset") ?? false;
1346
2158
  }
1347
2159
  function backgroundModeLabel(mode) {
1348
2160
  return mode === "default" ? "Default" : formatVariantLabel(mode);
@@ -1366,10 +2178,30 @@ function tokenSwatchCss(token) {
1366
2178
  return `rgb(var(--tb-${token}))`;
1367
2179
  }
1368
2180
  function gradientPreviewCss(gradientId) {
1369
- const preset = GRADIENT_PRESETS.find((candidate) => candidate.id === gradientId);
1370
- if (!preset) return "linear-gradient(135deg, rgb(var(--tb-primary)), rgb(var(--tb-secondary)))";
1371
- const stops = preset.stops.map((stop) => `${tokenSwatchCss(stop.color)} ${stop.position}%`).join(", ");
1372
- return preset.type === "radial" ? `radial-gradient(circle, ${stops})` : `linear-gradient(${preset.angle ?? 135}deg, ${stops})`;
2181
+ const preset = GRADIENT_PRESETS.find(
2182
+ (candidate) => candidate.id === gradientId
2183
+ );
2184
+ if (!preset) {
2185
+ return "linear-gradient(135deg, rgb(var(--tb-primary)), rgb(var(--tb-secondary)))";
2186
+ }
2187
+ return gradientToCss(
2188
+ preset,
2189
+ Object.fromEntries(
2190
+ preset.stops.map((stop) => [stop.color, tokenSwatchCss(stop.color)])
2191
+ )
2192
+ );
2193
+ }
2194
+ function readBackgroundColorPaletteSnapshot() {
2195
+ if (typeof window === "undefined" || typeof document === "undefined") {
2196
+ return null;
2197
+ }
2198
+ const rootStyle = window.getComputedStyle(document.documentElement);
2199
+ const bodyStyle = document.body ? window.getComputedStyle(document.body) : null;
2200
+ const entries = backgroundColorTokens.flatMap((token) => {
2201
+ const value = rootStyle.getPropertyValue(`--tb-${token}`).trim() || bodyStyle?.getPropertyValue(`--tb-${token}`).trim() || "";
2202
+ return value ? [[token, value]] : [];
2203
+ });
2204
+ return entries.length > 0 ? Object.fromEntries(entries) : null;
1373
2205
  }
1374
2206
  function formatTokenLabel(token) {
1375
2207
  return formatVariantLabel(token.replace(/\//g, " "));
@@ -1378,7 +2210,21 @@ function dedupeBackgroundImagePool(items) {
1378
2210
  const seen = /* @__PURE__ */ new Set();
1379
2211
  const deduped = [];
1380
2212
  for (const item of items) {
1381
- if (seen.has(item.mediaId)) continue;
2213
+ if (seen.has(item.mediaId)) {
2214
+ const existingIndex = deduped.findIndex(
2215
+ (candidate) => candidate.mediaId === item.mediaId
2216
+ );
2217
+ const existing = deduped[existingIndex];
2218
+ if (existing && !existing.mediaAsset && item.mediaAsset) {
2219
+ deduped[existingIndex] = {
2220
+ ...existing,
2221
+ mediaAsset: item.mediaAsset,
2222
+ label: existing.label ?? item.label,
2223
+ rank: existing.rank ?? item.rank
2224
+ };
2225
+ }
2226
+ continue;
2227
+ }
1382
2228
  seen.add(item.mediaId);
1383
2229
  deduped.push(item);
1384
2230
  }
@@ -1451,12 +2297,15 @@ function BoundaryControl({
1451
2297
  });
1452
2298
  });
1453
2299
  };
1454
- const applyBoundaryBreak = (direction) => (event) => {
2300
+ const applyBoundaryOption = (boundaryOptionId) => (event) => {
1455
2301
  event.preventDefault();
1456
2302
  event.stopPropagation();
1457
- const action = cyclePageDesignBoundaryBreak(boundary, direction);
1458
- if (!action) return;
1459
- saveBoundaryAction(action);
2303
+ saveBoundaryAction({
2304
+ kind: "set-boundary",
2305
+ previousBlockId: boundary.previousBlockId,
2306
+ nextBlockId: boundary.nextBlockId,
2307
+ boundaryOptionId
2308
+ });
1460
2309
  };
1461
2310
  const resetBoundaryBreak = (event) => {
1462
2311
  event.preventDefault();
@@ -1467,19 +2316,26 @@ function BoundaryControl({
1467
2316
  nextBlockId: boundary.nextBlockId
1468
2317
  });
1469
2318
  };
2319
+ const selectedVariant = sectionTransitionVariantForBoundaryOptionId(
2320
+ boundary.breakChoice.effectiveValue
2321
+ );
2322
+ const selectedFamily = sectionTransitionFamilyForBoundaryOptionId(
2323
+ boundary.breakChoice.effectiveValue
2324
+ ) ?? sectionTransitionFamilies[0];
2325
+ const variantOptions = selectedFamily.variants.length > 1 ? selectedFamily.variants : null;
1470
2326
  return /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20 h-16 -translate-y-1/2", children: /* @__PURE__ */ jsxs("div", { className: "group/boundary relative flex h-full items-center justify-center", children: [
1471
2327
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-sky-500/10 opacity-0 transition-opacity duration-150 group-hover/boundary:opacity-100 group-focus-within/boundary:opacity-100" }),
1472
2328
  /* @__PURE__ */ jsx(
1473
2329
  "button",
1474
2330
  {
1475
2331
  type: "button",
1476
- className: "pointer-events-auto relative z-10 rounded-full border border-white/20 bg-zinc-950/85 px-3 py-1 text-xs font-semibold text-white opacity-0 shadow-lg shadow-zinc-950/25 backdrop-blur transition hover:bg-zinc-900 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-sky-300 group-hover/boundary:opacity-100 group-focus-within/boundary:opacity-100",
2332
+ className: "pointer-events-auto relative z-10 flex h-12 min-w-32 items-center justify-center rounded-full opacity-0 transition hover:opacity-100 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-sky-300 group-hover/boundary:opacity-100 group-focus-within/boundary:opacity-100",
1477
2333
  "aria-controls": open ? panelId : void 0,
1478
2334
  "aria-expanded": open,
1479
2335
  "aria-haspopup": "dialog",
1480
2336
  "aria-label": "Customize section break",
1481
2337
  onClick: openPanel,
1482
- children: "Break"
2338
+ children: /* @__PURE__ */ jsx("span", { className: "rounded-full border border-white/20 bg-zinc-950/85 px-3 py-1 text-xs font-semibold text-white shadow-lg shadow-zinc-950/25 backdrop-blur transition hover:bg-zinc-900", children: "Break" })
1483
2339
  }
1484
2340
  ),
1485
2341
  open ? /* @__PURE__ */ jsxs(
@@ -1516,32 +2372,39 @@ function BoundaryControl({
1516
2372
  }
1517
2373
  )
1518
2374
  ] }),
1519
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border border-slate-200 bg-slate-50 p-2", children: [
1520
- /* @__PURE__ */ jsx(
1521
- "button",
2375
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-slate-200 bg-slate-50 p-2 text-center", children: [
2376
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: boundaryBreakLabel(boundary) }),
2377
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-slate-500", children: boundaryBreakModeLabel(boundary) })
2378
+ ] }),
2379
+ /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
2380
+ /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs font-medium text-slate-500", children: "Type" }),
2381
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 rounded-lg border border-slate-200 bg-slate-50 p-1", children: sectionTransitionFamilies.map((family) => /* @__PURE__ */ jsx(
2382
+ SectionTransitionFamilyButton,
1522
2383
  {
1523
- type: "button",
1524
- className: "rounded-md p-2 text-slate-600 transition hover:bg-white hover:text-slate-950 focus:outline-none focus:ring-2 focus:ring-sky-400",
1525
- "aria-label": "Previous section break",
1526
- onClick: applyBoundaryBreak("prev"),
1527
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": "true" })
1528
- }
1529
- ),
1530
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 text-center", children: [
1531
- /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: boundaryBreakLabel(boundary) }),
1532
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-slate-500", children: boundaryBreakModeLabel(boundary) })
2384
+ family,
2385
+ active: selectedFamily.id === family.id,
2386
+ onClick: applyBoundaryOption(
2387
+ defaultSectionTransitionVariantForFamily(family.id).boundaryOption.id
2388
+ )
2389
+ },
2390
+ family.id
2391
+ )) })
2392
+ ] }),
2393
+ variantOptions ? /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
2394
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 text-xs font-medium text-slate-500", children: [
2395
+ selectedFamily.label,
2396
+ " style"
1533
2397
  ] }),
1534
- /* @__PURE__ */ jsx(
1535
- "button",
2398
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 rounded-lg border border-slate-200 bg-slate-50 p-1", children: variantOptions.map((variant) => /* @__PURE__ */ jsx(
2399
+ SectionTransitionVariantButton,
1536
2400
  {
1537
- type: "button",
1538
- className: "rounded-md p-2 text-slate-600 transition hover:bg-white hover:text-slate-950 focus:outline-none focus:ring-2 focus:ring-sky-400",
1539
- "aria-label": "Next section break",
1540
- onClick: applyBoundaryBreak("next"),
1541
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" })
1542
- }
1543
- )
1544
- ] }),
2401
+ variant,
2402
+ active: selectedVariant?.boundaryOption.id === variant.boundaryOption.id,
2403
+ onClick: applyBoundaryOption(variant.boundaryOption.id)
2404
+ },
2405
+ variant.boundaryOption.id
2406
+ )) })
2407
+ ] }) : null,
1545
2408
  panelStatus ? /* @__PURE__ */ jsx("p", { id: panelStatus.id, className: "mt-2 text-xs text-slate-500", children: panelStatus.text }) : null,
1546
2409
  display.canReset ? /* @__PURE__ */ jsxs(
1547
2410
  "button",
@@ -1562,6 +2425,46 @@ function BoundaryControl({
1562
2425
  ) : null
1563
2426
  ] }) });
1564
2427
  }
2428
+ function SectionTransitionFamilyButton({
2429
+ family,
2430
+ active,
2431
+ onClick
2432
+ }) {
2433
+ return /* @__PURE__ */ jsx(
2434
+ "button",
2435
+ {
2436
+ type: "button",
2437
+ className: sectionTransitionChoiceClassName(active),
2438
+ "aria-pressed": active,
2439
+ "aria-label": `Use ${family.label} section break`,
2440
+ onClick,
2441
+ children: family.label
2442
+ }
2443
+ );
2444
+ }
2445
+ function SectionTransitionVariantButton({
2446
+ variant,
2447
+ active,
2448
+ onClick
2449
+ }) {
2450
+ return /* @__PURE__ */ jsx(
2451
+ "button",
2452
+ {
2453
+ type: "button",
2454
+ className: sectionTransitionChoiceClassName(active),
2455
+ "aria-pressed": active,
2456
+ "aria-label": `Use ${variant.label} section break`,
2457
+ onClick,
2458
+ children: variant.label
2459
+ }
2460
+ );
2461
+ }
2462
+ function sectionTransitionChoiceClassName(active) {
2463
+ return [
2464
+ "min-h-8 flex-1 rounded-md px-2.5 text-xs font-medium transition focus:outline-none focus:ring-2 focus:ring-sky-400",
2465
+ active ? "bg-slate-950 text-white shadow-sm" : "bg-transparent text-slate-600 hover:bg-white hover:text-slate-950"
2466
+ ].join(" ");
2467
+ }
1565
2468
  function boundaryBreakLabel(boundary) {
1566
2469
  return boundaryBreakDisplay(boundary).label;
1567
2470
  }
@@ -1625,7 +2528,9 @@ function resolveRuntimePreviewSource(input) {
1625
2528
  }
1626
2529
  return { kind: "preserve-server-runtime" };
1627
2530
  }
2531
+ var MAX_PREVIEW_BLOCK_DATA_BATCH_SIZE = 25;
1628
2532
  function EditablePageRenderer(props) {
2533
+ const { blockDataApiClient = null, ...pageRendererProps } = props;
1629
2534
  const { blocks } = useEntryEditorController();
1630
2535
  const uiStore = useEditorUiStoreInstance();
1631
2536
  const { state: pageDesignState } = usePreviewPageDesignEditorState();
@@ -1636,21 +2541,45 @@ function EditablePageRenderer(props) {
1636
2541
  (state) => state.aiOverlays.hasActiveProposals()
1637
2542
  );
1638
2543
  const allowInlineEditing = isEditingMode && !isStreaming && !hasProposals;
1639
- const isEntryTemplatePage = Boolean(props.dataContext?.contentEntry);
1640
- const headerClearancePx = usePreviewHeaderClearance();
2544
+ const isEntryTemplatePage = Boolean(pageRendererProps.dataContext?.contentEntry);
2545
+ const headerClearance = usePreviewHeaderClearance();
1641
2546
  const cacheRef = useRef(/* @__PURE__ */ new Map());
1642
- const draftsVersion = useEditorUiStore((state) => {
2547
+ const renderStateVersion = useEditorUiStore((state) => {
1643
2548
  const snapshots = state.drafts.inspect();
2549
+ const linkedEntries = state.linkedEntries.inspect();
1644
2550
  const overlayRevision = state.aiOverlays.revision;
1645
- return `${snapshots.map((s) => `${s.id}:${s.version}`).join("|")}|overlay:${overlayRevision}`;
2551
+ return [
2552
+ snapshots.map((s) => `${s.id}:${s.version}`).join("|"),
2553
+ linkedEntries.map((entry) => `${entry.meta.entryId}:${entry.version}`).join("|"),
2554
+ `overlay:${overlayRevision}`
2555
+ ].join("|");
1646
2556
  });
2557
+ const linkedEntryOverlays = useMemo(() => {
2558
+ const overlays = /* @__PURE__ */ new Map();
2559
+ for (const linkedEntry of uiStore.getState().linkedEntries.inspect()) {
2560
+ const content = linkedEntry.current ?? linkedEntry.baseline;
2561
+ if (!content) continue;
2562
+ overlays.set(linkedEntry.meta.entryId, {
2563
+ content,
2564
+ optimisticHash: linkedEntry.meta.optimisticHash
2565
+ });
2566
+ }
2567
+ return overlays;
2568
+ }, [uiStore, renderStateVersion]);
2569
+ const linkedEntryDataContext = useMemo(
2570
+ () => applyLinkedEntryRenderOverlayToDataContext(
2571
+ pageRendererProps.dataContext,
2572
+ linkedEntryOverlays
2573
+ ),
2574
+ [linkedEntryOverlays, pageRendererProps.dataContext]
2575
+ );
1647
2576
  const aiProjections = useMemo(
1648
2577
  () => previewProjectionsFromAiOverlays(uiStore.getState().aiOverlays),
1649
- [uiStore, draftsVersion]
2578
+ [uiStore, renderStateVersion]
1650
2579
  );
1651
2580
  const surfaceResolutionContext = useMemo(
1652
- () => hasThemePalette(props.theme) ? surfaceResolutionContextFromTheme(props.theme) : null,
1653
- [props.theme]
2581
+ () => hasThemePalette(pageRendererProps.theme) ? surfaceResolutionContextFromTheme(pageRendererProps.theme) : null,
2582
+ [pageRendererProps.theme]
1654
2583
  );
1655
2584
  const runtimePreviewSource = useMemo(
1656
2585
  () => resolveRuntimePreviewSource({
@@ -1673,19 +2602,34 @@ function EditablePageRenderer(props) {
1673
2602
  const mergedPage = useMemo(() => {
1674
2603
  return composePageFromBlocks(
1675
2604
  {
1676
- page: props.page,
2605
+ page: pageRendererProps.page,
1677
2606
  blockItems,
1678
2607
  uiStore,
1679
2608
  projections: renderProjections
1680
2609
  },
1681
2610
  cacheRef
1682
2611
  );
1683
- }, [blockItems, props.page, renderProjections, uiStore, draftsVersion]);
2612
+ }, [
2613
+ blockItems,
2614
+ pageRendererProps.page,
2615
+ renderProjections,
2616
+ uiStore,
2617
+ renderStateVersion
2618
+ ]);
2619
+ const renderPage = mergedPage ?? pageRendererProps.page;
1684
2620
  const wrapBlockWithProvider = useCallback(
1685
2621
  (blockId, rendered) => {
1686
- const blockIndex = blockItemsForRender.findIndex((b) => b.id === blockId);
1687
- const block = blockIndex >= 0 ? blockItemsForRender[blockIndex] ?? null : null;
1688
- const previousBlock = blockIndex > 0 ? blockItemsForRender[blockIndex - 1] ?? null : null;
2622
+ const renderedBlocks = renderPage?.blocks ?? [];
2623
+ const controllerBlockIndex = blockItemsForRender.findIndex(
2624
+ (b) => b.id === blockId
2625
+ );
2626
+ const renderedBlockIndex = renderedBlocks.findIndex(
2627
+ (b) => b.id === blockId
2628
+ );
2629
+ const blockIndex = controllerBlockIndex >= 0 ? controllerBlockIndex : renderedBlockIndex;
2630
+ const chromeBlocks = controllerBlockIndex >= 0 ? blockItemsForRender : renderedBlocks;
2631
+ const block = blockIndex >= 0 ? chromeBlocks[blockIndex] ?? null : null;
2632
+ const previousBlock = blockIndex > 0 ? chromeBlocks[blockIndex - 1] ?? null : null;
1689
2633
  const canonicalBlockIndex = blockItems.findIndex((b) => b.id === blockId);
1690
2634
  const canonicalPreviousBlock = canonicalBlockIndex > 0 ? blockItems[canonicalBlockIndex - 1] ?? null : null;
1691
2635
  const editorBlockId = unsafeAsEditorBlockId(blockId);
@@ -1726,7 +2670,12 @@ function EditablePageRenderer(props) {
1726
2670
  blockId,
1727
2671
  blockKind: block.kind,
1728
2672
  content,
1729
- headerClearancePx: shouldClearHeaderForBlock(blockItemsForRender, blockIndex) ? headerClearancePx : 0
2673
+ theme: pageRendererProps.theme,
2674
+ placement: planBlockToolbarPlacement({
2675
+ blocks: chromeBlocks,
2676
+ blockIndex,
2677
+ headerClearance
2678
+ })
1730
2679
  }
1731
2680
  )
1732
2681
  ] }) : null
@@ -1740,16 +2689,29 @@ function EditablePageRenderer(props) {
1740
2689
  aiProjections,
1741
2690
  blockItems,
1742
2691
  blockItemsForRender,
1743
- headerClearancePx,
2692
+ headerClearance,
2693
+ isEntryTemplatePage,
2694
+ pageRendererProps.theme,
1744
2695
  renderProjections,
2696
+ renderPage?.blocks,
1745
2697
  uiStore
1746
2698
  ]
1747
2699
  );
2700
+ const liveResolvedData = usePreviewBlockResolvedData({
2701
+ page: renderPage,
2702
+ dataContext: linkedEntryDataContext,
2703
+ apiClient: blockDataApiClient
2704
+ });
2705
+ const dataContext = useMemo(
2706
+ () => mergeResolvedDataContext(linkedEntryDataContext, liveResolvedData),
2707
+ [linkedEntryDataContext, liveResolvedData]
2708
+ );
1748
2709
  const rendererProps = {
1749
- ...props,
1750
- page: mergedPage,
1751
- registry: allowInlineEditing ? editorRegistry : props.registry ?? clientComponentRegistry,
1752
- wrapBlock: allowInlineEditing ? wrapBlockWithProvider : props.wrapBlock,
2710
+ ...pageRendererProps,
2711
+ page: renderPage,
2712
+ registry: allowInlineEditing ? editorRegistry : pageRendererProps.registry ?? clientComponentRegistry,
2713
+ wrapBlock: allowInlineEditing ? wrapBlockWithProvider : pageRendererProps.wrapBlock,
2714
+ dataContext,
1753
2715
  // Phase 10: the legacy `isEditingMode` flag forced the renderer into
1754
2716
  // draft-mode validation; `usePlaceholders` consolidates that into a
1755
2717
  // narrowly-scoped fallback. The editor's contract is unchanged: a user
@@ -1759,18 +2721,260 @@ function EditablePageRenderer(props) {
1759
2721
  // See `PageRenderer.tsx` `getCachedValidator(..., 'relaxed')`.
1760
2722
  usePlaceholders: true
1761
2723
  };
1762
- return /* @__PURE__ */ jsx(PageRenderer, { ...rendererProps });
2724
+ return /* @__PURE__ */ jsx(PageRenderer, { ...rendererProps });
2725
+ }
2726
+ function usePreviewBlockResolvedData(args) {
2727
+ const { page, dataContext, apiClient } = args;
2728
+ const cacheKey = useMemo(() => {
2729
+ if (!apiClient || !dataContext?.siteId) return null;
2730
+ return stableSerialize({
2731
+ siteId: dataContext.siteId,
2732
+ pageId: dataContext.pageId ?? null,
2733
+ previewStage: dataContext.previewStage ?? "published",
2734
+ blocks: page.blocks.map((block) => ({
2735
+ id: block.id,
2736
+ kind: block.kind,
2737
+ content: block.content ?? null,
2738
+ draftContent: block.draftContent ?? null,
2739
+ publishedContent: block.publishedContent ?? null
2740
+ }))
2741
+ });
2742
+ }, [apiClient, dataContext?.pageId, dataContext?.previewStage, dataContext?.siteId, page]);
2743
+ const [resolvedData, setResolvedData] = useState(null);
2744
+ useEffect(() => {
2745
+ if (!apiClient || !dataContext?.siteId || !cacheKey) {
2746
+ setResolvedData(null);
2747
+ return;
2748
+ }
2749
+ let cancelled = false;
2750
+ const context = buildPreviewBlockDataContext(dataContext);
2751
+ const loaderClient = createPreviewBlockDataApiClient(apiClient, context);
2752
+ void prefetchBlockData(page, context, {
2753
+ apiClient: loaderClient,
2754
+ isValidEndpoint: isSupportedLoaderEndpoint,
2755
+ onError: () => {
2756
+ }
2757
+ }).then((data) => {
2758
+ if (!cancelled) setResolvedData({ key: cacheKey, data });
2759
+ }).catch(() => {
2760
+ });
2761
+ return () => {
2762
+ cancelled = true;
2763
+ };
2764
+ }, [apiClient, cacheKey, dataContext, page]);
2765
+ return resolvedData?.data ?? null;
2766
+ }
2767
+ function buildPreviewBlockDataContext(dataContext) {
2768
+ return {
2769
+ siteId: dataContext.siteId,
2770
+ pageId: dataContext.pageId,
2771
+ previewStage: dataContext.previewStage,
2772
+ root: {
2773
+ ...dataContext.routes ? { routes: dataContext.routes } : {},
2774
+ ...dataContext.pagesByIdentifier ? { pagesByIdentifier: dataContext.pagesByIdentifier } : {},
2775
+ ...dataContext.entriesByIdentifier ? { entriesByIdentifier: dataContext.entriesByIdentifier } : {},
2776
+ occurrenceContext: dataContext.occurrenceContext ?? null,
2777
+ entrySubrouteContext: dataContext.entrySubrouteContext ?? null,
2778
+ contentEntry: dataContext.contentEntry ?? null,
2779
+ bookingOfferingPresets: dataContext.bookingOfferingPresets ?? []
2780
+ }
2781
+ };
2782
+ }
2783
+ function mergeResolvedDataContext(dataContext, liveResolvedData) {
2784
+ if (!dataContext || !liveResolvedData) return dataContext;
2785
+ return {
2786
+ ...dataContext,
2787
+ resolvedData: {
2788
+ ...dataContext.resolvedData ?? {},
2789
+ ...liveResolvedData
2790
+ }
2791
+ };
2792
+ }
2793
+ function createPreviewBlockDataApiClient(dashboardApiClient, context) {
2794
+ const batcher = createMicrobatcher({
2795
+ maxBatchSize: MAX_PREVIEW_BLOCK_DATA_BATCH_SIZE,
2796
+ runBatch: async (requests) => {
2797
+ if (!context.siteId) {
2798
+ return requests.reduce((acc, request) => {
2799
+ acc[request.id] = {
2800
+ ok: false,
2801
+ error: { code: "missing_site_id", message: "Missing siteId" }
2802
+ };
2803
+ return acc;
2804
+ }, {});
2805
+ }
2806
+ const envelope = await dashboardApiClient({
2807
+ endpoint: "batchBlockLoaders",
2808
+ params: {
2809
+ siteId: context.siteId,
2810
+ stage: context.previewStage
2811
+ },
2812
+ body: { requests }
2813
+ });
2814
+ const payload = unwrapResponse(envelope);
2815
+ return payload.results;
2816
+ }
2817
+ });
2818
+ return async ({ endpoint, params }) => {
2819
+ if (!isSupportedLoaderEndpoint(endpoint)) {
2820
+ throw new Error(`Unsupported block loader endpoint: ${endpoint}`);
2821
+ }
2822
+ const normalizedParams = normalizePublicApiLoaderParams(endpoint, params, context);
2823
+ if (isBatchableEndpoint(endpoint)) {
2824
+ const batchParams = toBatchParams(endpoint, normalizedParams);
2825
+ const batchKey = `${endpoint}:${stableSerialize(batchParams ?? null)}`;
2826
+ return batcher.getOrEnqueue(
2827
+ batchKey,
2828
+ (id) => buildBatchLoaderRequest(id, endpoint, batchParams)
2829
+ );
2830
+ }
2831
+ return callDirectLoaderApiEndpoint(
2832
+ dashboardApiClient,
2833
+ endpoint,
2834
+ normalizedParams
2835
+ );
2836
+ };
2837
+ }
2838
+ async function callDirectLoaderApiEndpoint(dashboardApiClient, endpoint, params) {
2839
+ switch (endpoint) {
2840
+ case "listPublishedEntries":
2841
+ return unwrapResponse(await dashboardApiClient({
2842
+ endpoint,
2843
+ params
2844
+ }));
2845
+ case "getPublishedEntryPreview":
2846
+ return unwrapResponse(await dashboardApiClient({
2847
+ endpoint,
2848
+ params
2849
+ }));
2850
+ case "listPublicPasses":
2851
+ return unwrapResponse(await dashboardApiClient({
2852
+ endpoint,
2853
+ params
2854
+ }));
2855
+ case "listPublicMemberships":
2856
+ return unwrapResponse(await dashboardApiClient({
2857
+ endpoint,
2858
+ params
2859
+ }));
2860
+ }
2861
+ }
2862
+ function isBatchableEndpoint(endpoint) {
2863
+ return endpoint === "listPublicEvents" || endpoint === "getPublicEventDetails" || endpoint === "listPublicCourses" || endpoint === "listPublicRouteCandidates" || endpoint === "listPublicTeamMembers" || endpoint === "listPublicProducts" || endpoint === "getPublicBookingServices" || endpoint === "getPublicFormById";
2864
+ }
2865
+ function buildBatchLoaderRequest(id, endpoint, params) {
2866
+ switch (endpoint) {
2867
+ case "listPublicEvents": {
2868
+ const typedParams = params;
2869
+ return typedParams ? { id, endpoint, params: typedParams } : { id, endpoint };
2870
+ }
2871
+ case "getPublicEventDetails": {
2872
+ const typedParams = params;
2873
+ return typedParams ? { id, endpoint, params: typedParams } : { id, endpoint };
2874
+ }
2875
+ case "listPublicCourses": {
2876
+ const typedParams = params;
2877
+ return typedParams ? { id, endpoint, params: typedParams } : { id, endpoint };
2878
+ }
2879
+ case "listPublicRouteCandidates":
2880
+ return { id, endpoint };
2881
+ case "listPublicProducts": {
2882
+ const typedParams = params;
2883
+ return typedParams ? { id, endpoint, params: typedParams } : { id, endpoint };
2884
+ }
2885
+ case "listPublicTeamMembers":
2886
+ return { id, endpoint };
2887
+ case "getPublicBookingServices": {
2888
+ const typedParams = params;
2889
+ return typedParams ? { id, endpoint, params: typedParams } : { id, endpoint };
2890
+ }
2891
+ case "getPublicFormById":
2892
+ return {
2893
+ id,
2894
+ endpoint,
2895
+ params: params ?? { formId: "" }
2896
+ };
2897
+ }
2898
+ }
2899
+ function stripUndefined(obj) {
2900
+ const out = {};
2901
+ for (const [key, value] of Object.entries(obj)) {
2902
+ if (value !== void 0) out[key] = value;
2903
+ }
2904
+ return out;
2905
+ }
2906
+ function normalizeIdsParam(ids) {
2907
+ if (!ids) return void 0;
2908
+ const parts = ids.split(",").map((id) => id.trim()).filter(Boolean).sort();
2909
+ return parts.length ? parts.join(",") : void 0;
2910
+ }
2911
+ function toBatchParams(endpoint, params) {
2912
+ if (endpoint === "listPublicEvents") {
2913
+ const p = stripUndefined({
2914
+ limit: params.limit || void 0,
2915
+ from: params.from || void 0,
2916
+ to: params.to || void 0,
2917
+ afterStartsAt: params.afterStartsAt || void 0,
2918
+ afterId: params.afterId || void 0,
2919
+ categoryIds: params.categoryIds || void 0,
2920
+ venueId: params.venueId || void 0,
2921
+ staffMemberId: params.staffMemberId || void 0,
2922
+ kind: params.kind || void 0,
2923
+ eventSurface: params.eventSurface || void 0,
2924
+ eventPresets: params.eventPresets || void 0,
2925
+ scheduleScope: params.scheduleScope || void 0,
2926
+ entryId: params.entryId || void 0,
2927
+ seriesId: params.seriesId || void 0,
2928
+ includeOccurrenceId: params.includeOccurrenceId || void 0,
2929
+ detailLevel: params.detailLevel || void 0,
2930
+ readProfile: params.readProfile || void 0
2931
+ });
2932
+ return Object.keys(p).length ? p : void 0;
2933
+ }
2934
+ if (endpoint === "getPublicEventDetails") {
2935
+ const p = stripUndefined({
2936
+ limit: params.limit || void 0,
2937
+ from: params.from || void 0,
2938
+ to: params.to || void 0,
2939
+ afterStartsAt: params.afterStartsAt || void 0,
2940
+ afterId: params.afterId || void 0,
2941
+ categoryIds: params.categoryIds || void 0,
2942
+ venueId: params.venueId || void 0,
2943
+ staffMemberId: params.staffMemberId || void 0,
2944
+ kind: params.kind || void 0,
2945
+ eventSurface: params.eventSurface || void 0,
2946
+ eventPresets: params.eventPresets || void 0,
2947
+ scheduleScope: params.scheduleScope || void 0,
2948
+ entryId: params.entryId || void 0,
2949
+ seriesId: params.seriesId || void 0,
2950
+ includeOccurrenceId: params.includeOccurrenceId || void 0
2951
+ });
2952
+ return Object.keys(p).length ? p : void 0;
2953
+ }
2954
+ if (endpoint === "listPublicCourses") {
2955
+ const p = stripUndefined({ limit: params.limit || void 0 });
2956
+ return Object.keys(p).length ? p : void 0;
2957
+ }
2958
+ if (endpoint === "listPublicProducts") {
2959
+ const p = stripUndefined({
2960
+ categorySelector: decodePublicProductCategorySelectorQuery(params) ?? void 0
2961
+ });
2962
+ return Object.keys(p).length ? p : void 0;
2963
+ }
2964
+ if (endpoint === "getPublicBookingServices") {
2965
+ const p = stripUndefined({ ids: normalizeIdsParam(params.ids || void 0) });
2966
+ return Object.keys(p).length ? p : void 0;
2967
+ }
2968
+ if (endpoint === "getPublicFormById") {
2969
+ return { formId: params.formId ?? "" };
2970
+ }
2971
+ return void 0;
1763
2972
  }
1764
2973
  function hasThemePalette(theme) {
1765
2974
  return Boolean(theme?.palette);
1766
2975
  }
1767
- function shouldClearHeaderForBlock(blocks, blockIndex) {
1768
- const block = blocks[blockIndex];
1769
- if (block?.kind !== "block.hero") return false;
1770
- return blocks.slice(0, blockIndex).every((candidate) => candidate.kind === "block.site-header");
1771
- }
1772
2976
  function usePreviewHeaderClearance() {
1773
- const [clearance, setClearance] = useState(0);
2977
+ const [clearance, setClearance] = useState({ kind: "none" });
1774
2978
  useEffect(() => {
1775
2979
  let observedHeader = null;
1776
2980
  let resizeObserver = null;
@@ -1785,12 +2989,19 @@ function usePreviewHeaderClearance() {
1785
2989
  }
1786
2990
  }
1787
2991
  if (!header) {
1788
- setClearance(0);
2992
+ setClearance(
2993
+ (current) => current.kind === "none" ? current : { kind: "none" }
2994
+ );
1789
2995
  return;
1790
2996
  }
1791
2997
  const style = window.getComputedStyle(header);
1792
2998
  const isOverlayHeader = style.position === "fixed" || style.position === "sticky";
1793
- setClearance(isOverlayHeader ? measuredOverlayHeaderClearancePx(header) : 0);
2999
+ const nextClearance = headerClearanceFromPx(
3000
+ isOverlayHeader ? measuredOverlayHeaderClearancePx(header) : 0
3001
+ );
3002
+ setClearance(
3003
+ (current) => headerClearanceEquals(current, nextClearance) ? current : nextClearance
3004
+ );
1794
3005
  };
1795
3006
  update();
1796
3007
  const mutationObserver = new MutationObserver(update);
@@ -1809,6 +3020,15 @@ function usePreviewHeaderClearance() {
1809
3020
  }, []);
1810
3021
  return clearance;
1811
3022
  }
3023
+ function headerClearanceEquals(left, right) {
3024
+ if (left.kind !== right.kind) return false;
3025
+ switch (left.kind) {
3026
+ case "none":
3027
+ return true;
3028
+ case "overlay":
3029
+ return right.kind === "overlay" && left.px === right.px;
3030
+ }
3031
+ }
1812
3032
  function findPreviewOverlayHeader() {
1813
3033
  return document.querySelector(
1814
3034
  ".block-site-header.rb-fixed, .block-site-header.rb-sticky"
@@ -2274,25 +3494,25 @@ var INITIAL_ONBOARDING_CONFIRMATION_STATE = {
2274
3494
  resendFeedback: null,
2275
3495
  statusLoadFailed: false
2276
3496
  };
2277
- function isRecord4(value) {
3497
+ function isRecord5(value) {
2278
3498
  return value !== null && typeof value === "object";
2279
3499
  }
2280
3500
  function getErrorMessageFromEnvelope(payload, fallback) {
2281
- const error = isRecord4(payload) ? payload.error : null;
2282
- const message = isRecord4(error) ? error.message : null;
3501
+ const error = isRecord5(payload) ? payload.error : null;
3502
+ const message = isRecord5(error) ? error.message : null;
2283
3503
  return typeof message === "string" && message.trim().length > 0 ? message : fallback;
2284
3504
  }
2285
3505
  function isLaunchReadiness(value) {
2286
- return isRecord4(value) && Array.isArray(value.requirements) && typeof value.pendingRequiredCount === "number" && typeof value.pendingRecommendedCount === "number" && typeof value.pendingOptionalCount === "number" && isRecord4(value.goLiveDecision) && (value.goLiveDecision.kind === "allowed" || value.goLiveDecision.kind === "blocked");
3506
+ return isRecord5(value) && Array.isArray(value.requirements) && typeof value.pendingRequiredCount === "number" && typeof value.pendingRecommendedCount === "number" && typeof value.pendingOptionalCount === "number" && isRecord5(value.goLiveDecision) && (value.goLiveDecision.kind === "allowed" || value.goLiveDecision.kind === "blocked");
2287
3507
  }
2288
3508
  function isOnboardingPreviewStatusResponse(value) {
2289
- return isRecord4(value) && typeof value.dashboardUnlocked === "boolean" && typeof value.requiresEmailConfirmation === "boolean" && isLaunchReadiness(value.launchReadiness);
3509
+ return isRecord5(value) && typeof value.dashboardUnlocked === "boolean" && typeof value.requiresEmailConfirmation === "boolean" && isLaunchReadiness(value.launchReadiness);
2290
3510
  }
2291
3511
  function isResendOnboardingPreviewConfirmationResponse(value) {
2292
- return isRecord4(value) && (value.result === "resent" || value.result === "already_confirmed") && typeof value.dashboardUnlocked === "boolean" && typeof value.requiresEmailConfirmation === "boolean" && isLaunchReadiness(value.launchReadiness);
3512
+ return isRecord5(value) && (value.result === "resent" || value.result === "already_confirmed") && typeof value.dashboardUnlocked === "boolean" && typeof value.requiresEmailConfirmation === "boolean" && isLaunchReadiness(value.launchReadiness);
2293
3513
  }
2294
3514
  function getSuccessData(payload, isData) {
2295
- if (!isRecord4(payload) || payload.success !== true || !isData(payload.data)) {
3515
+ if (!isRecord5(payload) || payload.success !== true || !isData(payload.data)) {
2296
3516
  return null;
2297
3517
  }
2298
3518
  return payload.data;
@@ -2758,313 +3978,6 @@ function PreviewAuthBanner({
2758
3978
  )
2759
3979
  ] });
2760
3980
  }
2761
- var SiteChromeCustomizeContext = createContext(null);
2762
- function SiteChromeCustomizeProvider({
2763
- children,
2764
- siteId,
2765
- apiClient,
2766
- theme,
2767
- themeId = null,
2768
- selectionId = null,
2769
- selectionVersion = null,
2770
- selectionUpdatedAt = null,
2771
- initialSelections,
2772
- headerSelectionContext,
2773
- footerSelectionContext,
2774
- siteStyleAccess = { kind: "hidden" }
2775
- }) {
2776
- const previewDesign = usePreviewDesignCoordinator();
2777
- const { clearPreview, previewTheme } = previewDesign;
2778
- const runnerRef = useRef(createOptimisticActionRunner());
2779
- const saveQueueRef = useRef(Promise.resolve());
2780
- const latestSelectionUpdatedAtRef = useRef(
2781
- initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
2782
- );
2783
- const initialSelectionVersion = initialSelections.selectionVersion ?? selectionVersion ?? 0;
2784
- const baseSnapshot = useMemo(
2785
- () => resolveStyleConfiguratorInitialSnapshot({
2786
- theme,
2787
- initialSelections: {
2788
- ...initialSelections,
2789
- selectionVersion: initialSelectionVersion,
2790
- selectionUpdatedAt: initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
2791
- },
2792
- headerSelectionContext,
2793
- footerSelectionContext
2794
- }),
2795
- [footerSelectionContext, headerSelectionContext, initialSelectionVersion, initialSelections, selectionUpdatedAt, theme]
2796
- );
2797
- const [state, setState] = useState(
2798
- () => baseSnapshot ? createStyleConfiguratorState(baseSnapshot) : null
2799
- );
2800
- const stateRef = useRef(state);
2801
- const clearPreviewRef = useRef(clearPreview);
2802
- useEffect(() => {
2803
- stateRef.current = state;
2804
- }, [state]);
2805
- useEffect(() => {
2806
- clearPreviewRef.current = clearPreview;
2807
- }, [clearPreview]);
2808
- useEffect(() => {
2809
- latestSelectionUpdatedAtRef.current = baseSnapshot?.selectionUpdatedAt ?? null;
2810
- }, [baseSnapshot?.selectionUpdatedAt]);
2811
- useEffect(() => {
2812
- if (!baseSnapshot) {
2813
- setState(null);
2814
- return;
2815
- }
2816
- setState(
2817
- (current) => current ? syncStyleConfiguratorState(current, baseSnapshot) : createStyleConfiguratorState(baseSnapshot)
2818
- );
2819
- }, [baseSnapshot]);
2820
- useEffect(() => {
2821
- const runner = runnerRef.current;
2822
- return () => {
2823
- runner.invalidate();
2824
- clearPreviewRef.current("site-chrome");
2825
- };
2826
- }, []);
2827
- const lookModels = useMemo(() => {
2828
- if (!state) return null;
2829
- const siteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
2830
- if (!siteStyle) return null;
2831
- return buildSiteChromeLookModels({
2832
- state,
2833
- siteStyle,
2834
- headerSelectionContext,
2835
- footerSelectionContext
2836
- });
2837
- }, [footerSelectionContext, headerSelectionContext, state]);
2838
- const applyAllowed = siteStyleAccess.kind === "apply-allowed";
2839
- const getSurfaceState = useCallback(
2840
- (surface) => {
2841
- if (!state || !lookModels) return { kind: "unavailable" };
2842
- if (surface === "header") {
2843
- const currentLayoutId = state.previewed.headerLayoutId;
2844
- const selectedLayout = lookModels.headerLayout.options.find(
2845
- (option) => option.id === currentLayoutId
2846
- );
2847
- const renderedLayout = lookModels.headerLayout.options.find(
2848
- (option) => option.id === lookModels.headerLayout.renderedLayoutId
2849
- );
2850
- const currentId2 = state.previewed.headerStyleId;
2851
- const selected2 = lookModels.headerStyle.options.find(
2852
- (option) => option.id === currentId2
2853
- );
2854
- const rendered2 = lookModels.headerStyle.options.find(
2855
- (option) => option.id === lookModels.headerStyle.renderedStyleId
2856
- );
2857
- return {
2858
- kind: "available",
2859
- surface: "header",
2860
- label: "Header",
2861
- currentLayoutId,
2862
- currentLayoutLabel: selectedLayout?.label ?? (renderedLayout ? `${renderedLayout.label} (default)` : "Unknown Layout"),
2863
- layoutOptions: lookModels.headerLayout.options,
2864
- currentId: currentId2,
2865
- currentLabel: selected2?.label ?? (rendered2 ? `${rendered2.label} (default)` : "Unknown Look"),
2866
- options: lookModels.headerStyle.options,
2867
- diagnostics: [],
2868
- saving: state.saveStatus.kind === "saving",
2869
- failure: state.saveStatus.kind === "failed" ? state.saveStatus.message : null,
2870
- applyAllowed
2871
- };
2872
- }
2873
- const currentId = state.previewed.footerLookId;
2874
- const selected = lookModels.footer.options.find((option) => option.id === currentId);
2875
- const rendered = lookModels.footer.options.find(
2876
- (option) => option.id === lookModels.footer.renderedLookId
2877
- );
2878
- return {
2879
- kind: "available",
2880
- surface: "footer",
2881
- label: "Footer",
2882
- currentId,
2883
- currentLabel: selected?.label ?? (currentId ? "Unknown Look" : `${rendered?.label ?? "Site style"} (default)`),
2884
- options: lookModels.footer.options,
2885
- diagnostics: lookModels.footer.diagnostics,
2886
- saving: state.saveStatus.kind === "saving",
2887
- failure: state.saveStatus.kind === "failed" ? state.saveStatus.message : null,
2888
- applyAllowed
2889
- };
2890
- },
2891
- [applyAllowed, lookModels, state]
2892
- );
2893
- const persistSelection = useCallback(
2894
- (surface, nextSnapshot) => {
2895
- const siteStyle = getCuratedSiteStyle(nextSnapshot.siteStyleId);
2896
- if (!siteStyle) return;
2897
- const compiled = planSiteStylePreviewChange({
2898
- siteStyle,
2899
- selection: nextSnapshot,
2900
- baseTheme: theme,
2901
- headerSelectionContext,
2902
- footerSelectionContext
2903
- });
2904
- void runnerRef.current.run({
2905
- optimistic: () => {
2906
- previewTheme("site-chrome", compiled.themeOverlay, "site-chrome");
2907
- setState((latest) => {
2908
- if (!latest) return latest;
2909
- return markApplySaving({ ...latest, previewed: nextSnapshot });
2910
- });
2911
- },
2912
- network: async () => {
2913
- const runSave = async () => {
2914
- const body = surface === "header" ? {
2915
- surface,
2916
- lookId: nextSnapshot.headerLookId,
2917
- layoutId: nextSnapshot.headerLayoutId,
2918
- styleId: nextSnapshot.headerStyleId,
2919
- theme: compiled.themeOverlay,
2920
- themeId: themeId ?? null,
2921
- selectionId: selectionId ?? null,
2922
- baseSelectionUpdatedAt: latestSelectionUpdatedAtRef.current
2923
- } : {
2924
- surface,
2925
- lookId: nextSnapshot.footerLookId,
2926
- theme: compiled.themeOverlay,
2927
- themeId: themeId ?? null,
2928
- selectionId: selectionId ?? null,
2929
- baseSelectionUpdatedAt: latestSelectionUpdatedAtRef.current
2930
- };
2931
- const response = await apiClient({
2932
- endpoint: "saveSiteChromeLookSelection",
2933
- params: { siteId },
2934
- body
2935
- });
2936
- const saved = unwrapResponse(response);
2937
- latestSelectionUpdatedAtRef.current = saved.selectionUpdatedAt ?? latestSelectionUpdatedAtRef.current;
2938
- return saved;
2939
- };
2940
- const queuedSave = saveQueueRef.current.catch(() => void 0).then(runSave);
2941
- saveQueueRef.current = queuedSave.then(
2942
- () => void 0,
2943
- () => void 0
2944
- );
2945
- return queuedSave;
2946
- },
2947
- onSuccess: (saved) => {
2948
- latestSelectionUpdatedAtRef.current = saved.selectionUpdatedAt ?? latestSelectionUpdatedAtRef.current;
2949
- const persisted = {
2950
- siteStyleId: nextSnapshot.siteStyleId,
2951
- selectionVersion: nextSnapshot.selectionVersion,
2952
- selectionUpdatedAt: saved.selectionUpdatedAt ?? nextSnapshot.selectionUpdatedAt ?? null,
2953
- buttonPersonalityId: compiled.resolvedSelections.buttonPersonalityId,
2954
- paletteVariantId: compiled.resolvedSelections.paletteVariantId,
2955
- paletteOverrides: nextSnapshot.paletteOverrides,
2956
- typographyPresetId: compiled.resolvedSelections.typographyPresetId,
2957
- typographyBodyFontOverride: compiled.resolvedSelections.typographyBodyFontOverride,
2958
- typographyHeadingFontOverride: compiled.resolvedSelections.typographyHeadingFontOverride,
2959
- headerLookId: saved.headerLookId ?? nextSnapshot.headerLookId,
2960
- headerLayoutId: saved.headerLayoutId ?? compiled.resolvedSelections.headerLayoutId,
2961
- headerStyleId: saved.headerStyleId ?? compiled.resolvedSelections.headerStyleId,
2962
- footerLookId: saved.footerLookId ?? nextSnapshot.footerLookId
2963
- };
2964
- setState((latest) => latest ? markApplySaved(latest, persisted) : latest);
2965
- },
2966
- onError: (error) => {
2967
- const message = error instanceof Error ? error.message : "Unable to apply your selection.";
2968
- setState((latest) => latest ? markApplyFailed(latest, message) : latest);
2969
- }
2970
- });
2971
- },
2972
- [
2973
- apiClient,
2974
- footerSelectionContext,
2975
- headerSelectionContext,
2976
- previewTheme,
2977
- selectionId,
2978
- siteId,
2979
- themeId,
2980
- theme
2981
- ]
2982
- );
2983
- const selectHeaderStyle = useCallback(
2984
- (styleId) => {
2985
- if (!applyAllowed) return;
2986
- const current = stateRef.current;
2987
- if (!current) return;
2988
- const nextState = previewHeaderStyle(current, styleId);
2989
- persistSelection("header", nextState.previewed);
2990
- },
2991
- [applyAllowed, persistSelection]
2992
- );
2993
- const selectHeaderLayout = useCallback(
2994
- (layoutId) => {
2995
- if (!applyAllowed) return;
2996
- const current = stateRef.current;
2997
- if (!current) return;
2998
- const nextState = previewHeaderLayout(current, layoutId);
2999
- persistSelection("header", nextState.previewed);
3000
- },
3001
- [applyAllowed, persistSelection]
3002
- );
3003
- const selectFooterLook = useCallback(
3004
- (lookId) => {
3005
- if (!applyAllowed) return;
3006
- const current = stateRef.current;
3007
- if (!current) return;
3008
- const nextState = previewFooterLook(current, planFooterLookSelection(lookId));
3009
- persistSelection("footer", nextState.previewed);
3010
- },
3011
- [applyAllowed, persistSelection]
3012
- );
3013
- const cycleSurfaceLook = useCallback(
3014
- (surface, direction) => {
3015
- const currentState = getSurfaceState(surface);
3016
- if (currentState.kind !== "available") return;
3017
- if (currentState.surface === "header") {
3018
- const next2 = cycleSiteChromeLookId(
3019
- currentState.options,
3020
- currentState.currentId,
3021
- direction
3022
- );
3023
- if (next2 !== void 0 && next2 !== null) selectHeaderStyle(next2);
3024
- return;
3025
- }
3026
- const next = cycleSiteChromeLookId(
3027
- currentState.options,
3028
- currentState.currentId,
3029
- direction
3030
- );
3031
- if (next !== void 0) selectFooterLook(next);
3032
- },
3033
- [getSurfaceState, selectFooterLook, selectHeaderStyle]
3034
- );
3035
- const cycleSurfaceLayout = useCallback(
3036
- (surface, direction) => {
3037
- const currentState = getSurfaceState(surface);
3038
- if (currentState.kind !== "available" || currentState.surface !== "header") {
3039
- return;
3040
- }
3041
- const next = cycleSiteChromeLookId(
3042
- currentState.layoutOptions,
3043
- currentState.currentLayoutId,
3044
- direction
3045
- );
3046
- if (next !== void 0 && next !== null) selectHeaderLayout(next);
3047
- },
3048
- [getSurfaceState, selectHeaderLayout]
3049
- );
3050
- const value = useMemo(
3051
- () => ({
3052
- getSurfaceState,
3053
- cycleSurfaceLook,
3054
- cycleSurfaceLayout
3055
- }),
3056
- [cycleSurfaceLayout, cycleSurfaceLook, getSurfaceState]
3057
- );
3058
- return /* @__PURE__ */ jsx(SiteChromeCustomizeContext.Provider, { value, children });
3059
- }
3060
- function planFooterLookSelection(lookId) {
3061
- const plan = planSiteChromeLookSelection({ surface: "footer", lookId });
3062
- if (plan.kind !== "footer") return lookId;
3063
- return plan.next.footerLookId;
3064
- }
3065
- function useSiteChromeCustomizeOptional() {
3066
- return useContext(SiteChromeCustomizeContext);
3067
- }
3068
3981
 
3069
3982
  // ../blocks/src/client/site-chrome.tsx
3070
3983
  function renderSiteChromeBlock(model, surface, theme, options = {}) {
@@ -3098,22 +4011,7 @@ function SiteChromeCustomizeButton({
3098
4011
  containerClassName = "pointer-events-auto absolute right-2 top-2 z-50 opacity-0 transition group-focus-within/site-chrome:opacity-100 group-hover/site-chrome:opacity-100"
3099
4012
  }) {
3100
4013
  const customize = useSiteChromeCustomizeOptional();
3101
- const customizePanelId = useId();
3102
- const customizeDescriptionId = `${customizePanelId}-description`;
3103
- const [customizeOpen, setCustomizeOpen] = useState(false);
3104
- const customizeButtonRef = useRef(null);
3105
- const closeCustomize = useCallback(() => {
3106
- setCustomizeOpen(false);
3107
- }, []);
3108
- const {
3109
- dialogRef: customizeDialogRef,
3110
- registerInitialFocus: registerCustomizeInitialFocus,
3111
- setReturnFocusTarget: setCustomizeReturnFocusTarget,
3112
- handleDialogKeyDown: handleCustomizeDialogKeyDown
3113
- } = useDialogFocusTrap({
3114
- open: customizeOpen,
3115
- onClose: closeCustomize
3116
- });
4014
+ const popover = useCustomizePopoverController();
3117
4015
  const surfaceState = customize?.getSurfaceState(surface) ?? {
3118
4016
  kind: "unavailable"
3119
4017
  };
@@ -3128,157 +4026,133 @@ function SiteChromeCustomizeButton({
3128
4026
  }
3129
4027
  return surfaceState.layoutOptions.filter((option) => option.available).length;
3130
4028
  }, [surfaceState]);
3131
- const toggleCustomize = (event) => {
3132
- event.preventDefault();
3133
- event.stopPropagation();
3134
- if (!customizeOpen) {
3135
- setCustomizeReturnFocusTarget(
3136
- event.currentTarget instanceof HTMLElement ? event.currentTarget : customizeButtonRef.current
3137
- );
4029
+ const controlPlan = useMemo(() => {
4030
+ if (surfaceState.kind !== "available") return { cycleRows: [] };
4031
+ return {
4032
+ cycleRows: [
4033
+ ...surfaceState.surface === "header" ? [
4034
+ {
4035
+ label: "Layout",
4036
+ valueLabel: surfaceState.currentLayoutLabel,
4037
+ previousLabel: `Previous ${surface} Layout`,
4038
+ nextLabel: `Next ${surface} Layout`,
4039
+ availability: cycleAvailabilityFromFlags({
4040
+ canCycle: cycleableLayoutOptionCount > 1,
4041
+ applyAllowed: surfaceState.applyAllowed
4042
+ }),
4043
+ onPrevious: () => customize?.cycleSurfaceLayout(surface, "prev"),
4044
+ onNext: () => customize?.cycleSurfaceLayout(surface, "next")
4045
+ }
4046
+ ] : [],
4047
+ {
4048
+ label: "Look",
4049
+ valueLabel: surfaceState.currentLabel,
4050
+ previousLabel: `Previous ${surface} Look`,
4051
+ nextLabel: `Next ${surface} Look`,
4052
+ availability: cycleAvailabilityFromFlags({
4053
+ canCycle: cycleableOptionCount > 1,
4054
+ applyAllowed: surfaceState.applyAllowed
4055
+ }),
4056
+ onPrevious: () => customize?.cycleSurfaceLook(surface, "prev"),
4057
+ onNext: () => customize?.cycleSurfaceLook(surface, "next")
4058
+ }
4059
+ ]
4060
+ };
4061
+ }, [
4062
+ customize,
4063
+ cycleableLayoutOptionCount,
4064
+ cycleableOptionCount,
4065
+ surface,
4066
+ surfaceState
4067
+ ]);
4068
+ return /* @__PURE__ */ jsxs(
4069
+ PreviewChromeFloatingControl,
4070
+ {
4071
+ anchorSelector: "[data-preview-site-chrome-surface]",
4072
+ placement: (anchorBox) => planSiteChromeCustomizePlacement({ surface, anchorBox }),
4073
+ fallbackClassName: containerClassName,
4074
+ forceVisible: popover.open,
4075
+ children: [
4076
+ /* @__PURE__ */ jsx(
4077
+ CustomizePillButton,
4078
+ {
4079
+ buttonRef: popover.buttonRef,
4080
+ ariaLabel: `Customize ${surface}`,
4081
+ open: popover.open,
4082
+ controlsId: popover.panelId,
4083
+ onClick: popover.toggle,
4084
+ disabled: !hasCustomizeControls
4085
+ }
4086
+ ),
4087
+ popover.open && surfaceState.kind === "available" ? /* @__PURE__ */ jsxs(
4088
+ CustomizePopoverShell,
4089
+ {
4090
+ panelId: popover.panelId,
4091
+ descriptionId: popover.descriptionId,
4092
+ dialogLabel: `Customize ${surface}`,
4093
+ eyebrow: "Site",
4094
+ title: surfaceState.label,
4095
+ dialogRef: popover.dialogRef,
4096
+ closeButtonRef: popover.registerInitialFocus,
4097
+ onClose: popover.close,
4098
+ onKeyDown: popover.handleDialogKeyDown,
4099
+ children: [
4100
+ controlPlan.cycleRows.map((row) => /* @__PURE__ */ jsx(CustomizeCycleRow, { ...row }, row.label)),
4101
+ surfaceState.surface === "header" ? /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 border-t pt-3", children: [
4102
+ /* @__PURE__ */ jsxs("label", { className: "block text-xs font-medium text-muted-foreground", children: [
4103
+ /* @__PURE__ */ jsx("span", { children: "Display" }),
4104
+ /* @__PURE__ */ jsx(
4105
+ "select",
4106
+ {
4107
+ className: "mt-1 h-8 w-full rounded-md border bg-background px-2 text-sm text-foreground disabled:cursor-not-allowed disabled:opacity-50",
4108
+ value: surfaceState.brandDisplay,
4109
+ onChange: (event) => {
4110
+ event.stopPropagation();
4111
+ customize?.setHeaderBrandDisplay(
4112
+ event.currentTarget.value
4113
+ );
4114
+ },
4115
+ disabled: !surfaceState.applyAllowed,
4116
+ children: surfaceState.brandDisplayOptions.map((option) => /* @__PURE__ */ jsx(
4117
+ "option",
4118
+ {
4119
+ value: option.value,
4120
+ disabled: option.disabled,
4121
+ children: option.label
4122
+ },
4123
+ option.value
4124
+ ))
4125
+ }
4126
+ )
4127
+ ] }),
4128
+ /* @__PURE__ */ jsxs("label", { className: "block text-xs font-medium text-muted-foreground", children: [
4129
+ /* @__PURE__ */ jsx("span", { children: "Logo size" }),
4130
+ /* @__PURE__ */ jsx(
4131
+ "select",
4132
+ {
4133
+ className: "mt-1 h-8 w-full rounded-md border bg-background px-2 text-sm text-foreground disabled:cursor-not-allowed disabled:opacity-50",
4134
+ value: surfaceState.logoSize,
4135
+ onChange: (event) => {
4136
+ event.stopPropagation();
4137
+ customize?.setHeaderLogoSize(
4138
+ event.currentTarget.value
4139
+ );
4140
+ },
4141
+ disabled: !surfaceState.applyAllowed,
4142
+ children: surfaceState.logoSizeOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
4143
+ }
4144
+ )
4145
+ ] })
4146
+ ] }) : null,
4147
+ surfaceState.diagnostics.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-xs text-amber-700", children: surfaceState.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx("li", { children: diagnostic }, diagnostic)) }) : null,
4148
+ surfaceState.failure ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-700", children: surfaceState.failure }) : null,
4149
+ !surfaceState.applyAllowed ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Preview only - ask an admin to apply." }) : null
4150
+ ]
4151
+ }
4152
+ ) : null
4153
+ ]
3138
4154
  }
3139
- setCustomizeOpen((open) => !open);
3140
- };
3141
- return /* @__PURE__ */ jsxs("div", { className: containerClassName, children: [
3142
- /* @__PURE__ */ jsx(
3143
- "button",
3144
- {
3145
- ref: customizeButtonRef,
3146
- type: "button",
3147
- onClick: toggleCustomize,
3148
- className: previewCustomizePillButtonClassName,
3149
- "aria-label": `Customize ${surface}`,
3150
- "aria-expanded": customizeOpen,
3151
- "aria-haspopup": "dialog",
3152
- "aria-controls": customizeOpen ? customizePanelId : void 0,
3153
- disabled: !hasCustomizeControls,
3154
- children: surfaceState.kind === "available" && surfaceState.saving ? "Saving..." : "Customize"
3155
- }
3156
- ),
3157
- customizeOpen && surfaceState.kind === "available" ? /* @__PURE__ */ jsxs(
3158
- "div",
3159
- {
3160
- id: customizePanelId,
3161
- className: "builder-dashboard-chrome-theme fixed inset-x-3 bottom-[calc(var(--builder-editor-chrome-offset,0px)+1rem)] z-[70] rounded-lg border bg-background p-3 text-foreground shadow-xl sm:absolute sm:inset-x-auto sm:bottom-auto sm:right-0 sm:top-9 sm:w-72",
3162
- role: "dialog",
3163
- "aria-modal": "true",
3164
- "aria-label": `Customize ${surface}`,
3165
- "aria-describedby": customizeDescriptionId,
3166
- ref: customizeDialogRef,
3167
- onKeyDown: handleCustomizeDialogKeyDown,
3168
- onClick: (event) => event.stopPropagation(),
3169
- children: [
3170
- /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
3171
- /* @__PURE__ */ jsxs("div", { children: [
3172
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: "Site" }),
3173
- /* @__PURE__ */ jsx(
3174
- "div",
3175
- {
3176
- id: customizeDescriptionId,
3177
- className: "text-sm font-semibold",
3178
- children: surfaceState.label
3179
- }
3180
- )
3181
- ] }),
3182
- /* @__PURE__ */ jsx(
3183
- "button",
3184
- {
3185
- ref: registerCustomizeInitialFocus,
3186
- type: "button",
3187
- className: "rounded-full px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
3188
- onClick: (event) => {
3189
- event.preventDefault();
3190
- event.stopPropagation();
3191
- closeCustomize();
3192
- },
3193
- children: "Close"
3194
- }
3195
- )
3196
- ] }),
3197
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
3198
- surfaceState.surface === "header" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3199
- /* @__PURE__ */ jsxs("div", { children: [
3200
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Layout" }),
3201
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: surfaceState.currentLayoutLabel })
3202
- ] }),
3203
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
3204
- /* @__PURE__ */ jsx(
3205
- "button",
3206
- {
3207
- type: "button",
3208
- onClick: (event) => {
3209
- event.preventDefault();
3210
- event.stopPropagation();
3211
- customize?.cycleSurfaceLayout(surface, "prev");
3212
- },
3213
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
3214
- "aria-label": `Previous ${surface} Layout`,
3215
- disabled: surfaceState.saving || !surfaceState.applyAllowed || cycleableLayoutOptionCount <= 1,
3216
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
3217
- }
3218
- ),
3219
- /* @__PURE__ */ jsx(
3220
- "button",
3221
- {
3222
- type: "button",
3223
- onClick: (event) => {
3224
- event.preventDefault();
3225
- event.stopPropagation();
3226
- customize?.cycleSurfaceLayout(surface, "next");
3227
- },
3228
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
3229
- "aria-label": `Next ${surface} Layout`,
3230
- disabled: surfaceState.saving || !surfaceState.applyAllowed || cycleableLayoutOptionCount <= 1,
3231
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
3232
- }
3233
- )
3234
- ] })
3235
- ] }) : null,
3236
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3237
- /* @__PURE__ */ jsxs("div", { children: [
3238
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Look" }),
3239
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: surfaceState.currentLabel })
3240
- ] }),
3241
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
3242
- /* @__PURE__ */ jsx(
3243
- "button",
3244
- {
3245
- type: "button",
3246
- onClick: (event) => {
3247
- event.preventDefault();
3248
- event.stopPropagation();
3249
- customize?.cycleSurfaceLook(surface, "prev");
3250
- },
3251
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
3252
- "aria-label": `Previous ${surface} Look`,
3253
- disabled: surfaceState.saving || !surfaceState.applyAllowed || cycleableOptionCount <= 1,
3254
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
3255
- }
3256
- ),
3257
- /* @__PURE__ */ jsx(
3258
- "button",
3259
- {
3260
- type: "button",
3261
- onClick: (event) => {
3262
- event.preventDefault();
3263
- event.stopPropagation();
3264
- customize?.cycleSurfaceLook(surface, "next");
3265
- },
3266
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
3267
- "aria-label": `Next ${surface} Look`,
3268
- disabled: surfaceState.saving || !surfaceState.applyAllowed || cycleableOptionCount <= 1,
3269
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
3270
- }
3271
- )
3272
- ] })
3273
- ] }),
3274
- surfaceState.diagnostics.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-xs text-amber-700", children: surfaceState.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx("li", { children: diagnostic }, diagnostic)) }) : null,
3275
- surfaceState.failure ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-700", children: surfaceState.failure }) : null,
3276
- !surfaceState.applyAllowed ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Preview only - ask an admin to apply." }) : null
3277
- ] })
3278
- ]
3279
- }
3280
- ) : null
3281
- ] });
4155
+ );
3282
4156
  }
3283
4157
  function SiteChromeEditableSurface({
3284
4158
  surface,
@@ -3296,7 +4170,7 @@ function renderEditableSurface(children, surface, options) {
3296
4170
  const child = childArray.length === 1 ? childArray[0] : null;
3297
4171
  if (isValidElement(child) && child.type !== Fragment$1 && (isHostElement(child) || options.preserveSingleComponentRoot || isEditableSurfaceForwardingElement(child))) {
3298
4172
  return cloneElement(child, {
3299
- className: joinClassNames(child.props.className, "group/site-chrome relative"),
4173
+ className: joinClassNames2(child.props.className, "group/site-chrome relative"),
3300
4174
  "data-preview-site-chrome-surface": surface,
3301
4175
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
3302
4176
  child.props.children,
@@ -3335,7 +4209,7 @@ function isHostElement(child) {
3335
4209
  function isEditableSurfaceForwardingElement(child) {
3336
4210
  return typeof child.type !== "string" && child.type !== Fragment$1 && child.type[EDITABLE_SURFACE_FORWARDING_PROP] === true;
3337
4211
  }
3338
- function joinClassNames(...values) {
4212
+ function joinClassNames2(...values) {
3339
4213
  const className = values.filter(Boolean).join(" ");
3340
4214
  return className.length > 0 ? className : void 0;
3341
4215
  }
@@ -3517,13 +4391,27 @@ function renderPreviewDocument({
3517
4391
  }
3518
4392
  return null;
3519
4393
  }
4394
+ function LinkedEntryEditEventBridge() {
4395
+ const uiStore = useEditorUiStoreInstance();
4396
+ useEffect(() => {
4397
+ const handleLinkedEntryEdit = (event) => {
4398
+ if (!isLinkedEntryEditEvent(event)) return;
4399
+ uiStore.getState().linkedEntries.open(event.detail);
4400
+ };
4401
+ window.addEventListener(LINKED_ENTRY_EDIT_EVENT, handleLinkedEntryEdit);
4402
+ return () => {
4403
+ window.removeEventListener(LINKED_ENTRY_EDIT_EVENT, handleLinkedEntryEdit);
4404
+ };
4405
+ }, [uiStore]);
4406
+ return null;
4407
+ }
3520
4408
  var previewContainerResponsiveEnabled = process.env.NEXT_PUBLIC_ENABLE_PREVIEW_CONTAINER_RESPONSIVE !== "false";
3521
- var PreviewEditorSidebar = React9.lazy(async () => {
3522
- const mod = await import('./PreviewEditorSidebar-56NMJMIK.mjs');
4409
+ var PreviewEditorSidebar = React10.lazy(async () => {
4410
+ const mod = await import('./PreviewEditorSidebar-RDCMEXIJ.mjs');
3523
4411
  return { default: mod.PreviewEditorSidebar };
3524
4412
  });
3525
- var PreviewEditorUI = React9.lazy(async () => {
3526
- const mod = await import('./PreviewEditorUI-27KJVEET.mjs');
4413
+ var PreviewEditorUI = React10.lazy(async () => {
4414
+ const mod = await import('./PreviewEditorUI-NYGQ6K7B.mjs');
3527
4415
  return { default: mod.PreviewEditorUI };
3528
4416
  });
3529
4417
  var previewPaneContainmentStyle = {
@@ -3739,7 +4627,7 @@ function PreviewShellLayout({
3739
4627
  {
3740
4628
  className: "relative flex h-full min-h-0 flex-col overflow-auto bg-muted/30",
3741
4629
  style: previewPaneContainmentStyle,
3742
- children: /* @__PURE__ */ jsx(
4630
+ children: /* @__PURE__ */ jsx(PreviewChromeLayerProvider, { children: /* @__PURE__ */ jsx(
3743
4631
  PreviewThemedSiteDocument,
3744
4632
  {
3745
4633
  useThemeScope,
@@ -3756,7 +4644,7 @@ function PreviewShellLayout({
3756
4644
  siteChromeRenderModel,
3757
4645
  children
3758
4646
  }
3759
- )
4647
+ ) })
3760
4648
  }
3761
4649
  );
3762
4650
  const rightPane = /* @__PURE__ */ jsx(
@@ -3764,7 +4652,7 @@ function PreviewShellLayout({
3764
4652
  {
3765
4653
  className: `${PREVIEW_CHROME_THEME_CLASS_NAME} flex h-full min-h-0 flex-col overflow-hidden ${surfaceDocument.surfaceClassName ?? ""}`,
3766
4654
  ...getPreviewChromeSurfaceDataProps(surfaceDocument),
3767
- children: /* @__PURE__ */ jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
4655
+ children: /* @__PURE__ */ jsx(React10.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
3768
4656
  PreviewEditorSidebar,
3769
4657
  {
3770
4658
  surfaceId,
@@ -3819,7 +4707,7 @@ function PreviewShellLayout({
3819
4707
  ) })
3820
4708
  }
3821
4709
  );
3822
- const editorUI = /* @__PURE__ */ jsx(React9.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
4710
+ const editorUI = /* @__PURE__ */ jsx(React10.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
3823
4711
  PreviewEditorUI,
3824
4712
  {
3825
4713
  inMaintenanceMode: maintenanceModeEnabled,
@@ -3836,43 +4724,7 @@ function PreviewShellLayout({
3836
4724
  surfaceId
3837
4725
  }
3838
4726
  );
3839
- if (!isOpen) {
3840
- return /* @__PURE__ */ jsx(PreviewThemeOverlayProvider, { baseTheme: theme, children: /* @__PURE__ */ jsx(PreviewDesignCoordinatorProvider, { children: /* @__PURE__ */ jsxs(
3841
- SiteChromeCustomizeProvider,
3842
- {
3843
- siteId,
3844
- apiClient,
3845
- theme,
3846
- themeId,
3847
- selectionId: themeSelectionId,
3848
- selectionVersion: themeSelectionVersion,
3849
- selectionUpdatedAt: themeSelectionUpdatedAt,
3850
- initialSelections: {
3851
- selectionVersion: themeSelectionVersion,
3852
- selectionUpdatedAt: themeSelectionUpdatedAt,
3853
- buttonPersonalityId: themeSelectionButtonPersonalityId,
3854
- paletteVariantId: themeSelectionPaletteVariantId,
3855
- paletteOverrides: themeSelectionPaletteOverrides,
3856
- headerLayoutId: themeSelectionHeaderLayoutId,
3857
- headerStyleId: themeSelectionHeaderStyleId,
3858
- typographyPresetId: themeSelectionTypographyPresetId,
3859
- typographyBodyFontOverride: themeSelectionTypographyBodyFontOverride,
3860
- typographyHeadingFontOverride: themeSelectionTypographyHeadingFontOverride,
3861
- headerLookId: themeSelectionHeaderLookId,
3862
- footerLookId: themeSelectionFooterLookId
3863
- },
3864
- headerSelectionContext: themeSelectionHeaderContext,
3865
- footerSelectionContext: themeSelectionFooterContext,
3866
- siteStyleAccess,
3867
- children: [
3868
- editorUI,
3869
- previewBanner,
3870
- /* @__PURE__ */ jsx("div", { className: "fixed inset-0 overflow-hidden bg-background", children: leftPane })
3871
- ]
3872
- }
3873
- ) }) });
3874
- }
3875
- return /* @__PURE__ */ jsx(PreviewThemeOverlayProvider, { baseTheme: theme, children: /* @__PURE__ */ jsx(PreviewDesignCoordinatorProvider, { children: /* @__PURE__ */ jsxs(
4727
+ return /* @__PURE__ */ jsx(PreviewThemeOverlayProvider, { baseTheme: theme, children: /* @__PURE__ */ jsx(PreviewDesignCoordinatorProvider, { children: /* @__PURE__ */ jsx(
3876
4728
  SiteChromeCustomizeProvider,
3877
4729
  {
3878
4730
  siteId,
@@ -3899,10 +4751,11 @@ function PreviewShellLayout({
3899
4751
  headerSelectionContext: themeSelectionHeaderContext,
3900
4752
  footerSelectionContext: themeSelectionFooterContext,
3901
4753
  siteStyleAccess,
3902
- children: [
4754
+ children: /* @__PURE__ */ jsxs(PreviewBlockCustomizeSelectionProvider, { children: [
4755
+ /* @__PURE__ */ jsx(LinkedEntryEditEventBridge, {}),
3903
4756
  editorUI,
3904
4757
  previewBanner,
3905
- /* @__PURE__ */ jsx("div", { className: "fixed inset-0 overflow-hidden bg-background", children: /* @__PURE__ */ jsx(
4758
+ /* @__PURE__ */ jsx("div", { className: "fixed inset-0 overflow-hidden bg-background", children: isOpen ? /* @__PURE__ */ jsx(
3906
4759
  ResizableSplitPane,
3907
4760
  {
3908
4761
  left: leftPane,
@@ -3917,8 +4770,8 @@ function PreviewShellLayout({
3917
4770
  onSplitChange: (split) => handleSidebarWidthChange(100 - split),
3918
4771
  splitPercent: 100 - sidebarWidth
3919
4772
  }
3920
- ) })
3921
- ]
4773
+ ) : leftPane })
4774
+ ] })
3922
4775
  }
3923
4776
  ) }) });
3924
4777
  }
@@ -3999,7 +4852,7 @@ function PreviewShell({
3999
4852
  initialBlocks,
4000
4853
  pageMetadata,
4001
4854
  apiClient,
4002
- children: /* @__PURE__ */ jsx(
4855
+ children: /* @__PURE__ */ jsx(PreviewDesignHistoryOrderProvider, { children: /* @__PURE__ */ jsx(
4003
4856
  PreviewPageDesignEditorStateProvider,
4004
4857
  {
4005
4858
  apiClient,
@@ -4063,7 +4916,7 @@ function PreviewShell({
4063
4916
  }
4064
4917
  )
4065
4918
  }
4066
- )
4919
+ ) })
4067
4920
  }
4068
4921
  ) })
4069
4922
  }
@@ -4078,17 +4931,6 @@ function defaultDesignEditorEligibility(input) {
4078
4931
  }
4079
4932
  return { kind: "unsupported" };
4080
4933
  }
4081
-
4082
- // ../preview-next/src/client/preview/previewSurface.ts
4083
- function resolvePreviewSurfaceIdFromSearch(search) {
4084
- return parseSurfaceId(new URLSearchParams(search).get(PREVIEW_SURFACE_PARAM));
4085
- }
4086
- function resolvePreviewSurfaceId() {
4087
- if (typeof window === "undefined") {
4088
- return null;
4089
- }
4090
- return resolvePreviewSurfaceIdFromSearch(window.location.search);
4091
- }
4092
4934
  function SdkPreviewModeRuntime(props) {
4093
4935
  const {
4094
4936
  apiBaseUrl,
@@ -4102,7 +4944,7 @@ function SdkPreviewModeRuntime(props) {
4102
4944
  useThemeScope = true
4103
4945
  } = props;
4104
4946
  const resolvedDashboardBaseUrl = dashboardBaseUrl ?? deriveDashboardBaseUrlFromApiBaseUrl(apiBaseUrl);
4105
- const surfaceId = props.surfaceId ?? resolvePreviewSurfaceId() ?? DEFAULT_SURFACE_ID;
4947
+ const surfaceId = props.surfaceId ?? DEFAULT_SURFACE_ID;
4106
4948
  const theme = content.theme;
4107
4949
  const page = content.page;
4108
4950
  const routeMap = content.routeMap ?? void 0;