@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,241 @@
1
1
  "use client";
2
- import { resolvePreviewDashboardSetupHandoff, buildPreviewDashboardSetupUrl, resolvePreviewSidebarChromeModel, resolvePreviewEditorGuidanceModel, canInspectSiteStyle, usePreviewDesignCoordinator, resolveStyleConfiguratorInitialSnapshot, createStyleConfiguratorState, createOptimisticActionRunner, syncStyleConfiguratorState, isPreviewingChange, planSiteStylePreviewChange, openRow, previewSiteStyle, applyResolvedSelections, previewButtonPersonality, previewHeaderLayout, previewHeaderStyle, planSiteChromeLookSelection, previewFooterLook, previewPaletteVariant, previewTypographyPreset, setTypographyBodyFontOverride, setTypographyHeadingFontOverride, clearTypographyFontOverrides, setPaletteOverrides, toggleRow, cancelPreview, dismissWarnings, dismissNotices, markApplyFailed, markApplySaved, markApplySaving, canApplySiteStyle, buildSiteChromeLookModels, cycleSiteChromeLookId, cycleableSiteChromeOptions } from './chunk-LCY32PXF.mjs';
3
- import { KeyboardCode, getBlockDefinition, CSS, useSdkConfig, unwrapResponse, parseSiteIconStyleGuideVersionId, parseBlockIconValue, listBlockDescriptors, useSensor, PointerSensor, useSensors, deleteBlock, createEditorRemoveBlockCommand, unsafeAsEditorBlockId, DndContext, closestCenter, Button, Plus, useDroppable, useDraggable, useCombinedRefs, isKeyboardEvent, createCustomBlockRegistry, Card, cn, Collapsible, CollapsibleContent, listContentTypes, mergeBlockDescriptorsWithRegistry, createBlock, createEditorReorderBlocksCommand, reorderBlocks, mutateBlockContent, useDndContext, useUniqueId, useIsomorphicLayoutEffect, EmptyState, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Search, getClientRect, resolveBlockTitle, CardHeader, GripVertical, ChevronRight, CardTitle, ArrowUp, ArrowDown, DropdownMenu, DropdownMenuTrigger, EllipsisVertical, DropdownMenuContent, DropdownMenuItem, Settings, DropdownMenuSeparator, Sparkles, RotateCcw, ImagePlus, Copy, Trash2, DialogFooter, DialogClose, CardContent, createEditorInsertBlockCommand, LoaderCircle, ChevronDown, getFeatureCardIconCompletion, toFeatureCardIconSuggestionInputs, getBlockIconSemanticFallbackId, suggestSemanticIconForFeatureCard, getGeneratedBlockIconStyleState, assertNever, suggestFeatureCardSemanticIcons, applyFeatureCardIconSuggestions, createFeatureCardIconKey, Textarea, Callout, CalloutDescription, normalEditorCuratedPromotedFieldPaths, resolveVisiblePromotedFieldPathsForEditorBlock, createStoreBackend, validateBlockManifestIssues, CalloutTitle, unfinishedBlockCopy, recoveryConflictCopy, ManifestForm, unsafeAsEditorBlockValues, Box, createManifestFormAdapter, getModalConfig, isNonEmptyValue, ScrollableDialogContent, FormRenderer, Tabs, TabsList, TabsTrigger, TabsContent, mergeManifestWithSdkOptions, augmentManifestWithStyleGroups, lucide_react_exports, isThemeV2NormalEditorRawStylePath, getFieldIntent, isHeaderField, isThemeV2NormalEditorRawStyleFieldName, useEntryController, useEditorUiStoreInstance, useUISelector, resolvePreviewSidebarTab, BackgroundGradientField, BackgroundColorField, X, omitRouteTitleFieldFromManifest, getRouteMetadataManifestForType, createManifestValidation, previewAssistantEnabled, useField, FieldFrame, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, resolveIconPickerPreset, isIconPickerPreset, getIconPickerOptionsForPreset, isKnownIconName, Combobox, getIconComponent, useBackendWatch, useAsyncPickerOptions, shouldShowPickerSearch, normalizePickerSearchThreshold, Badge, resolvePickerSelectionLabel, useMediaEditorOptional, useRichTextEditor, RichTextEditorSurface, ThemeScope, useBlockContext, resolveMediaSlotContract, asRecord, parseAspectRatioValue, MediaEditor, PUBLIC_FILE_FIELD_KINDS, CollapsibleTrigger, ContentFieldsForm, getCuratedSiteStyle, paletteOverridesSchema, findButtonPersonality, findPaletteVariant, findTypographyPreset, getHeaderLayout, getHeaderStyle, findFooterLook, footerLookCatalog, isObjectRecord, EMPTY_DOC, getRichTextImagePositionFromTarget, insertOrReplaceRichTextImage, resolveVideoSelectionIntentForMediaField, emitMediaCropDebug, Pencil, foldSubroute, SUBROUTE_KINDS, Smartphone, Tablet, Monitor, Maximize2, DropdownMenuLabel, expectParsedBrandedId, parseSiteId, buildThemeRuntime, PaletteProvider, MediaEditorProvider, curatedSiteStyles, ChevronLeft, requireEntriesForCuratedChoices, buttonPersonalities, typographyPresets, paletteVariants, resolveImageUrlWithContext, Root2, List, Trigger, Content, Input, Check, Label, isInternalResolvedLinkValue, evaluateTypographyFontSlot, Separator, classifyKnownMimeType, toRoutableLinkPayload, useMediaEditor, MediaPickerMini, MediaPickerDialog, Surface, ColorPicker, getDirectImageUrlForMediaAsset, isSvgMimeType } from './chunk-V47ZU33I.mjs';
4
- import { usePreviewAuth, getDashboardUrl } from './chunk-K2IIF34C.mjs';
5
- import './chunk-R7POPVJR.mjs';
6
- import React, { createContext, memo, useMemo, useState, useRef, useEffect, forwardRef, useCallback, useImperativeHandle, useContext, useSyncExternalStore, useLayoutEffect, useReducer, useTransition, useId } from 'react';
2
+ import { KeyboardCode, getBlockDefinition, CSS, useSdkConfig, unwrapResponse, parseSiteIconStyleGuideVersionId, parseBlockIconValue, listBlockDescriptors, useSensor, PointerSensor, useSensors, deleteBlock, createEditorRemoveBlockCommand, unsafeAsEditorBlockId, DndContext, closestCenter, Button, Plus, useDroppable, useDraggable, useCombinedRefs, isKeyboardEvent, createCustomBlockRegistry, Card, cn, Collapsible, CollapsibleContent, listContentTypes, mergeBlockDescriptorsWithRegistry, createBlock, createEditorReorderBlocksCommand, reorderBlocks, mutateBlockContent, useDndContext, useUniqueId, useIsomorphicLayoutEffect, EmptyState, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Search, getClientRect, resolveBlockTitle, CardHeader, GripVertical, ChevronRight, CardTitle, ArrowUp, ArrowDown, DropdownMenu, DropdownMenuTrigger, EllipsisVertical, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuItem, Copy, Trash2, DialogFooter, DialogClose, CardContent, createEditorInsertBlockCommand, LoaderCircle, ChevronDown, getFeatureCardIconCompletion, toFeatureCardIconSuggestionInputs, getBlockIconSemanticFallbackId, suggestSemanticIconForFeatureCard, getGeneratedBlockIconStyleState, assertNever, createManifestFormAdapter, getModalConfig, createStoreBackend, validateBlockManifestIssues, isNonEmptyValue, Settings, ScrollableDialogContent, FormRenderer, Tabs, TabsList, TabsTrigger, TabsContent, RotateCcw, ImagePlus, Sparkles, Textarea, Callout, CalloutDescription, normalEditorCuratedPromotedFieldPaths, resolveVisiblePromotedFieldPathsForEditorBlock, CalloutTitle, unfinishedBlockCopy, recoveryConflictCopy, ManifestForm, unsafeAsEditorBlockValues, Box, mergeManifestWithSdkOptions, augmentManifestWithStyleGroups, suggestFeatureCardSemanticIcons, applyFeatureCardIconSuggestions, createFeatureCardIconKey, lucide_react_exports, isThemeV2NormalEditorRawStylePath, getFieldIntent, isHeaderField, isThemeV2NormalEditorRawStyleFieldName, useEditorUiStoreInstance, useUISelector, useField, useMediaEditorOptional, useRichTextEditor, FieldFrame, RichTextEditorSurface, ThemeScope, isObjectRecord, EMPTY_DOC, useMediaEditor, MediaPickerMini, MediaPickerDialog, useAsyncPickerOptions, resolvePickerSelectionLabel, shouldShowPickerSearch, normalizePickerSearchThreshold, Combobox, Badge, useBlockContext, resolveMediaSlotContract, asRecord, parseAspectRatioValue, MediaEditor, PUBLIC_FILE_FIELD_KINDS, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, useBackendWatch, resolveIconPickerPreset, isIconPickerPreset, getIconPickerOptionsForPreset, isKnownIconName, getIconComponent, CommandMultiSelect, getRichTextImagePositionFromTarget, insertOrReplaceRichTextImage, emitMediaCropDebug, Pencil, resolveVideoSelectionIntentForMediaField, siteOptionSources, evaluateVisibility, Root2, List, Trigger, Content, Input, Check, Label, isInternalResolvedLinkValue, resolveImageUrlWithContext, getDirectImageUrlForMediaAsset, isSvgMimeType, toRoutableLinkPayload, classifyKnownMimeType } from './chunk-WUHKQBB6.mjs';
3
+ import React2, { createContext, memo, useMemo, useState, useRef, useEffect, forwardRef, useCallback, useImperativeHandle, useContext, useSyncExternalStore, useLayoutEffect, Fragment as Fragment$1, useReducer } from 'react';
7
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { useRouter } from 'next/navigation';
5
+
6
+ // ../preview-next/src/client/preview/previewChromeSurfacePolicy.ts
7
+ var PREVIEW_CHROME_SURFACE_POLICIES = {
8
+ core: {
9
+ banner: {
10
+ tone: "default",
11
+ bannerClassName: "border border-emerald-200/70 bg-white/95 shadow-xl backdrop-blur supports-[backdrop-filter]:bg-white/92",
12
+ summaryClassName: "rounded-md border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-600",
13
+ dialogItemClassName: "rounded-md border border-slate-200 bg-slate-50 px-3 py-2",
14
+ titleClassName: "text-sm font-semibold tracking-tight text-foreground [font-family:var(--font-heading)]",
15
+ descriptionClassName: "text-sm text-muted-foreground [font-family:var(--font-body)]",
16
+ detailClassName: "text-xs text-muted-foreground [font-family:var(--font-body)]",
17
+ summaryTitleClassName: "font-medium text-foreground [font-family:var(--font-body)]",
18
+ summaryBodyClassName: "mt-1 text-muted-foreground [font-family:var(--font-body)]",
19
+ secondaryButtonClassName: "",
20
+ primaryButtonClassName: "",
21
+ dialogBodyClassName: "space-y-3 text-sm text-muted-foreground [font-family:var(--font-body)]",
22
+ dialogItemTitleClassName: "font-medium text-foreground [font-family:var(--font-heading)]",
23
+ dialogItemMetaClassName: "ml-2 text-xs font-normal uppercase tracking-wide text-muted-foreground [font-family:var(--font-body)]",
24
+ dialogItemDescriptionClassName: "mt-1 text-sm text-muted-foreground [font-family:var(--font-body)]",
25
+ ctaLabel: "Complete setup in dashboard",
26
+ copyByState: {
27
+ awaiting_confirmation: {
28
+ title: "Your draft preview is ready",
29
+ description: "Explore your site here, then confirm your email to unlock the dashboard and finish launch setup.",
30
+ detail: "Once your email is confirmed, Complete setup in dashboard will take you straight to the remaining checklist.",
31
+ checklistNote: "Email confirmation is the only blocker before dashboard access. The rest of the launch checklist lives in the dashboard."
32
+ },
33
+ dashboard_ready: {
34
+ title: "Your dashboard is unlocked",
35
+ description: "Keep exploring here, then use Complete setup in dashboard to review your pages, add offerings, connect payments, and finish launch setup.",
36
+ detail: "The dashboard is the place to complete the remaining checklist and get your site ready to launch.",
37
+ checklistNote: "The remaining launch steps are tracked in the same checklist you will see in the dashboard."
38
+ },
39
+ dashboard_resume: {
40
+ title: "Your draft site is ready",
41
+ description: "Keep exploring here, then use Complete setup in dashboard to review your pages, add offerings, connect payments, and finish launch setup.",
42
+ detail: "The dashboard is the normal place to finish setup for this site.",
43
+ checklistNote: "The remaining launch steps are tracked in the same checklist you will see in the dashboard."
44
+ }
45
+ },
46
+ dialog: {
47
+ title: "What still happens in the dashboard",
48
+ description: "Your preview is ready, but launch steps still happen in the dashboard so you can finish setup safely.",
49
+ pendingIntro: "Your preview is live now, but the launch checklist stays in the dashboard so the final setup steps happen in the right place.",
50
+ unlockedIntro: "Your preview is live now, and the same checklist will follow you into the dashboard when you are ready to finish setup.",
51
+ outro: "You can keep exploring this preview now, then jump back into the dashboard when you're ready to finish setup."
52
+ }
53
+ },
54
+ sidebar: {
55
+ tone: "default",
56
+ rootClassName: "bg-background",
57
+ headerClassName: "border-b",
58
+ guidance: {
59
+ title: "Make quick edits in preview",
60
+ description: "Use preview for copy and layout tweaks. Complete setup in dashboard to add offerings, connect payments, and finish the rest of your launch checklist.",
61
+ ctaLabel: "Complete setup in dashboard",
62
+ cardClassName: "rounded-lg border border-emerald-200/70 bg-emerald-50/70 px-3 py-3"
63
+ }
64
+ }
65
+ },
66
+ macadamia: {
67
+ banner: {
68
+ tone: "studio",
69
+ bannerClassName: "border border-border bg-background shadow-xl",
70
+ summaryClassName: "rounded-xl border border-border bg-surface px-3 py-2 text-xs text-foreground",
71
+ dialogItemClassName: "rounded-xl border border-border bg-surface px-3 py-2",
72
+ titleClassName: "text-sm font-semibold tracking-tight text-foreground [font-family:var(--font-heading)]",
73
+ descriptionClassName: "text-sm text-muted-foreground [font-family:var(--font-body)]",
74
+ detailClassName: "text-xs text-muted-foreground [font-family:var(--font-body)]",
75
+ summaryTitleClassName: "font-medium text-foreground [font-family:var(--font-body)]",
76
+ summaryBodyClassName: "mt-1 text-muted-foreground [font-family:var(--font-body)]",
77
+ secondaryButtonClassName: "border border-border bg-surface text-foreground hover:bg-button-outline-hover",
78
+ primaryButtonClassName: "border border-primary bg-primary text-primary-foreground hover:bg-primary-hover",
79
+ dialogBodyClassName: "space-y-3 text-sm text-muted-foreground [font-family:var(--font-body)]",
80
+ dialogItemTitleClassName: "font-medium text-foreground [font-family:var(--font-heading)]",
81
+ dialogItemMetaClassName: "ml-2 text-xs font-normal uppercase tracking-wide text-muted-foreground [font-family:var(--font-body)]",
82
+ dialogItemDescriptionClassName: "mt-1 text-sm text-muted-foreground [font-family:var(--font-body)]",
83
+ ctaLabel: "Complete setup in Macadamia",
84
+ copyByState: {
85
+ awaiting_confirmation: {
86
+ title: "Confirm your email to carry on",
87
+ description: "We've created the bones of your site. To carry on finishing setup, and to edit your site, you need to confirm your email to activate your account.",
88
+ detail: "",
89
+ checklistNote: ""
90
+ },
91
+ dashboard_ready: {
92
+ title: "Here's your site preview",
93
+ description: "We've created the bones of your site. Have a look around. If you like, you can add your site content here, or you can go straight to your dashboard to complete your setup. You'll be able to edit your site at any time, so don't worry if it's not perfect right now.",
94
+ detail: "",
95
+ checklistNote: ""
96
+ },
97
+ dashboard_resume: {
98
+ title: "Your studio site is ready",
99
+ description: "Keep exploring here, then use Complete setup in Macadamia to review your pages, add offerings, connect payments, and finish launch setup.",
100
+ detail: "Macadamia is the normal place to finish setup for this studio site.",
101
+ checklistNote: "The remaining launch steps are tracked in the same checklist you will see in Macadamia."
102
+ }
103
+ },
104
+ dialog: {
105
+ title: "What still happens in Macadamia",
106
+ description: "Your preview is ready, but launch steps still happen in Macadamia so you can finish setup safely.",
107
+ pendingIntro: "Your studio preview is live now, but the launch checklist stays in Macadamia so the final setup steps happen in the right place.",
108
+ unlockedIntro: "Your studio preview is live now, and the same checklist will follow you into Macadamia when you are ready to finish setup.",
109
+ outro: "You can keep exploring this preview now, then jump back into Macadamia when you're ready to finish setup."
110
+ }
111
+ },
112
+ sidebar: {
113
+ tone: "studio",
114
+ rootClassName: "bg-stone-50/80",
115
+ headerClassName: "border-b border-amber-200/70 bg-amber-50/70",
116
+ guidance: {
117
+ title: "Polish your studio preview",
118
+ description: "Use preview for copy and layout tweaks. Complete setup in Macadamia to add offerings, connect payments, and finish the rest of your launch checklist.",
119
+ ctaLabel: "Complete setup in Macadamia",
120
+ cardClassName: "rounded-2xl border border-amber-200/80 bg-gradient-to-br from-amber-50 via-white to-orange-50 px-4 py-4 shadow-sm"
121
+ }
122
+ }
123
+ }
124
+ };
125
+ function getPreviewChromeSurfacePolicy(surfaceId) {
126
+ return PREVIEW_CHROME_SURFACE_POLICIES[surfaceId];
127
+ }
128
+ function resolvePreviewSetupBannerModel(input) {
129
+ if (!input.bannerState) {
130
+ return null;
131
+ }
132
+ const policy = getPreviewChromeSurfacePolicy(input.surfaceId).banner;
133
+ return {
134
+ tone: policy.tone,
135
+ bannerClassName: policy.bannerClassName,
136
+ summaryClassName: policy.summaryClassName,
137
+ dialogItemClassName: policy.dialogItemClassName,
138
+ titleClassName: policy.titleClassName,
139
+ descriptionClassName: policy.descriptionClassName,
140
+ detailClassName: policy.detailClassName,
141
+ summaryTitleClassName: policy.summaryTitleClassName,
142
+ summaryBodyClassName: policy.summaryBodyClassName,
143
+ secondaryButtonClassName: policy.secondaryButtonClassName,
144
+ primaryButtonClassName: policy.primaryButtonClassName,
145
+ dialogBodyClassName: policy.dialogBodyClassName,
146
+ dialogItemTitleClassName: policy.dialogItemTitleClassName,
147
+ dialogItemMetaClassName: policy.dialogItemMetaClassName,
148
+ dialogItemDescriptionClassName: policy.dialogItemDescriptionClassName,
149
+ ctaLabel: policy.ctaLabel,
150
+ copy: policy.copyByState[input.bannerState],
151
+ dialogTitle: policy.dialog.title,
152
+ dialogDescription: policy.dialog.description,
153
+ dialogIntro: input.dashboardUnlocked ? policy.dialog.unlockedIntro : policy.dialog.pendingIntro,
154
+ dialogOutro: policy.dialog.outro
155
+ };
156
+ }
157
+ function resolvePreviewSidebarChromeModel(surfaceId) {
158
+ const policy = getPreviewChromeSurfacePolicy(surfaceId).sidebar;
159
+ return {
160
+ tone: policy.tone,
161
+ rootClassName: policy.rootClassName,
162
+ headerClassName: policy.headerClassName
163
+ };
164
+ }
165
+ function resolvePreviewEditorGuidanceModel(input) {
166
+ if (!input.canEdit || !input.hasDashboardSetupHandoff) {
167
+ return null;
168
+ }
169
+ const policy = getPreviewChromeSurfacePolicy(input.surfaceId).sidebar;
170
+ return {
171
+ tone: policy.tone,
172
+ title: policy.guidance.title,
173
+ description: policy.guidance.description,
174
+ ctaLabel: policy.guidance.ctaLabel,
175
+ cardClassName: policy.guidance.cardClassName
176
+ };
177
+ }
178
+
179
+ // ../preview-next/src/client/preview/sanitizeDashboardPath.ts
180
+ function sanitizeDashboardPath(value) {
181
+ if (!value) return null;
182
+ const trimmed = value.trim();
183
+ if (!trimmed || /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
184
+ return null;
185
+ }
186
+ const normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
187
+ try {
188
+ const parsed = new URL(normalized, "https://preview.local");
189
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+
195
+ // ../preview-next/src/client/preview/previewDashboardHandoff.ts
196
+ function resolvePreviewDashboardSetupHandoffFromSearch(search) {
197
+ const params = new URLSearchParams(search);
198
+ if (params.get("preview_handoff") !== "dashboard_setup") {
199
+ return null;
200
+ }
201
+ const dashboardPath = sanitizeDashboardPath(params.get("preview_dashboard"));
202
+ if (!dashboardPath) {
203
+ return null;
204
+ }
205
+ return {
206
+ kind: "dashboard_setup",
207
+ dashboardPath
208
+ };
209
+ }
210
+ function resolvePreviewDashboardSetupHandoff() {
211
+ if (typeof window === "undefined") {
212
+ return null;
213
+ }
214
+ return resolvePreviewDashboardSetupHandoffFromSearch(window.location.search);
215
+ }
216
+ function buildPreviewDashboardSetupUrl(dashboardBaseUrl, handoff) {
217
+ if (!dashboardBaseUrl || !handoff) {
218
+ return null;
219
+ }
220
+ return new URL(handoff.dashboardPath, dashboardBaseUrl).toString();
221
+ }
222
+
223
+ // ../preview-next/src/client/preview/siteStyleAccess.ts
224
+ function resolveSiteStyleAccess(session) {
225
+ if (session?.kind === "userSession" && session.canManage) {
226
+ return { kind: "apply-allowed" };
227
+ }
228
+ if (session?.kind === "userSession" && session.canEdit) {
229
+ return { kind: "preview-only" };
230
+ }
231
+ return { kind: "hidden" };
232
+ }
233
+ function canInspectSiteStyle(access) {
234
+ return access.kind !== "hidden";
235
+ }
236
+ function canApplySiteStyle(access) {
237
+ return access.kind === "apply-allowed";
238
+ }
9
239
 
10
240
  // ../content-model/src/referenceFields.ts
11
241
  var BUILT_IN_REFERENCE_KINDS = [
@@ -317,65 +547,38 @@ function normalizeFieldUi(value) {
317
547
  }
318
548
  return value;
319
549
  }
320
- var ButtonWidgetContext = createContext(null);
321
- function ButtonWidgetProvider({ buttonSystem, children }) {
322
- const value = useMemo(() => {
323
- const variants = buttonSystem ? buttonSystem.variants.filter((v) => v.enabled).map((v) => ({
324
- id: v.id,
325
- name: v.name,
326
- priority: v.priority
327
- })).sort((a, b) => a.priority - b.priority) : [];
328
- const defaultVariant = buttonSystem?.variants.find((v) => v.priority === 1)?.id ?? null;
329
- return {
330
- variants,
331
- defaultVariant,
332
- buttonSystem
333
- };
334
- }, [buttonSystem]);
335
- return /* @__PURE__ */ jsx(ButtonWidgetContext.Provider, { value, children });
336
- }
337
- function useButtonWidgetContext() {
338
- const context = useContext(ButtonWidgetContext);
339
- if (!context) {
340
- throw new Error("useButtonWidgetContext must be used within ButtonWidgetProvider");
341
- }
342
- return context;
343
- }
344
- var DEFAULT_VARIANTS = [
345
- { id: "primary", name: "Primary", priority: 1 },
346
- { id: "secondary", name: "Secondary", priority: 2 },
347
- { id: "outline", name: "Outline", priority: 3 }
348
- ];
349
- function ButtonVariantField({ path, backend, config }) {
350
- const { value, error, setValue } = useField(path, backend);
351
- const buttonContext = useButtonWidgetContext();
352
- const variants = buttonContext.variants.length > 0 ? buttonContext.variants : DEFAULT_VARIANTS;
353
- const options = variants.map((v) => ({ value: v.id, label: v.name }));
354
- const isUnknownVariant = value && !options.some((opt) => opt.value === value);
355
- if (isUnknownVariant && true) {
356
- console.warn(
357
- `[ButtonVariantField] Unknown button variant "${value}" at path "${path}". Available variants: ${options.map((o) => o.value).join(", ")}. The button may not render with expected styling.`
358
- );
359
- }
360
- return /* @__PURE__ */ jsx(
361
- FieldFrame,
362
- {
363
- label: config.label,
364
- description: config.description,
365
- error: error?.message,
366
- children: () => /* @__PURE__ */ jsxs(
367
- Select,
368
- {
369
- value: typeof value === "string" && value !== "" ? value : void 0,
370
- onValueChange: setValue,
371
- children: [
372
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: `Select ${config.label.toLowerCase()}` }) }),
373
- /* @__PURE__ */ jsx(SelectContent, { children: options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
374
- ]
375
- }
376
- )
377
- }
550
+ var PreviewBlockCustomizeSelectionContext = createContext(null);
551
+ function PreviewBlockCustomizeSelectionProvider({
552
+ children
553
+ }) {
554
+ const uiStore = useEditorUiStoreInstance();
555
+ const previewInspection = useUISelector(uiStore, (state) => state.previewInspection);
556
+ const selection = useMemo(
557
+ () => previewInspection.kind === "block-customize" ? {
558
+ kind: "selected",
559
+ blockId: previewInspection.blockId,
560
+ requestId: previewInspection.requestId
561
+ } : { kind: "none" },
562
+ [previewInspection]
563
+ );
564
+ const inspectBlock = useCallback((blockId) => {
565
+ uiStore.getState().inspectBlock(blockId);
566
+ }, [uiStore]);
567
+ const clearInspection = useCallback(() => {
568
+ uiStore.getState().clearPreviewInspection();
569
+ }, [uiStore]);
570
+ const value = useMemo(
571
+ () => ({
572
+ selection,
573
+ inspectBlock,
574
+ clearInspection
575
+ }),
576
+ [clearInspection, inspectBlock, selection]
378
577
  );
578
+ return /* @__PURE__ */ jsx(PreviewBlockCustomizeSelectionContext.Provider, { value, children });
579
+ }
580
+ function usePreviewBlockCustomizeSelectionOptional() {
581
+ return useContext(PreviewBlockCustomizeSelectionContext);
379
582
  }
380
583
  function useStoreSelector(store, selector, isEqual = Object.is) {
381
584
  const cacheRef = useRef(null);
@@ -513,7 +716,7 @@ function getItemGap$1(clientRects, index, activeIndex) {
513
716
  return nextRect ? nextRect.top - (currentRect.top + currentRect.height) : previousRect ? currentRect.top - (previousRect.top + previousRect.height) : 0;
514
717
  }
515
718
  var ID_PREFIX = "Sortable";
516
- var Context = /* @__PURE__ */ React.createContext({
719
+ var Context = /* @__PURE__ */ React2.createContext({
517
720
  activeIndex: -1,
518
721
  containerId: ID_PREFIX,
519
722
  disableTransforms: false,
@@ -575,7 +778,7 @@ function SortableContext(_ref) {
575
778
  // eslint-disable-next-line react-hooks/exhaustive-deps
576
779
  [activeIndex, containerId, disabled.draggable, disabled.droppable, disableTransforms, items, overIndex, droppableRects, useDragOverlay, strategy]
577
780
  );
578
- return React.createElement(Context.Provider, {
781
+ return React2.createElement(Context.Provider, {
579
782
  value: contextValue
580
783
  }, children);
581
784
  }
@@ -1927,8 +2130,10 @@ function useBlockReorder({
1927
2130
  return { reorderId, handleMove, onDragEnd };
1928
2131
  }
1929
2132
 
1930
- // ../editor-blocks/src/block-editor/actionControlMode.ts
1931
- var DEFAULT_BLOCK_ACTION_CONTROL_MODE = "inline";
2133
+ // ../editor-blocks/src/block-editor/actionPresentation.ts
2134
+ var DEFAULT_BLOCK_ACTION_PRESENTATION = {
2135
+ kind: "inline"
2136
+ };
1932
2137
 
1933
2138
  // ../editor-blocks/src/block-editor/reorderControlMode.ts
1934
2139
  var DEFAULT_REORDER_CONTROL_MODE = "full";
@@ -2485,16 +2690,30 @@ function GeneratedIconSetDialog({
2485
2690
  }) {
2486
2691
  const [state, dispatch] = useReducer(reducer, { kind: "idle" });
2487
2692
  const [styleNote, setStyleNote] = useState("");
2693
+ const activeGenerationRef = useRef(null);
2488
2694
  const canGenerate = Boolean(onGenerate) && cards.length > 0 && state.kind !== "generating";
2489
2695
  const canApply = Boolean(onApply) && state.kind === "preview_ready";
2696
+ function abortActiveGeneration() {
2697
+ activeGenerationRef.current?.abort();
2698
+ activeGenerationRef.current = null;
2699
+ }
2700
+ useEffect(() => () => {
2701
+ activeGenerationRef.current?.abort();
2702
+ activeGenerationRef.current = null;
2703
+ }, []);
2490
2704
  async function generate() {
2491
2705
  if (!onGenerate || cards.length === 0) return;
2706
+ abortActiveGeneration();
2707
+ const generation = new AbortController();
2708
+ activeGenerationRef.current = generation;
2492
2709
  dispatch({ type: "generating" });
2493
2710
  try {
2494
2711
  const result = await onGenerate({
2495
2712
  cards,
2496
- userStyleNote: styleNote.trim().length > 0 ? styleNote.trim() : null
2713
+ userStyleNote: styleNote.trim().length > 0 ? styleNote.trim() : null,
2714
+ signal: generation.signal
2497
2715
  });
2716
+ if (activeGenerationRef.current !== generation || generation.signal.aborted) return;
2498
2717
  if (result.kind === "preview_ready") {
2499
2718
  const validation = validateGeneratedIconSetPreview(result.preview, cards);
2500
2719
  if (validation.kind === "invalid") {
@@ -2506,10 +2725,16 @@ function GeneratedIconSetDialog({
2506
2725
  }
2507
2726
  dispatch({ type: "failed", message: result.message });
2508
2727
  } catch (error) {
2728
+ if (activeGenerationRef.current !== generation || generation.signal.aborted) return;
2509
2729
  dispatch({ type: "failed", message: getGenerationErrorMessage(error) });
2730
+ } finally {
2731
+ if (activeGenerationRef.current === generation) {
2732
+ activeGenerationRef.current = null;
2733
+ }
2510
2734
  }
2511
2735
  }
2512
2736
  function close(nextOpen) {
2737
+ if (!nextOpen) abortActiveGeneration();
2513
2738
  onOpenChange(nextOpen);
2514
2739
  if (!nextOpen) dispatch({ type: "reset" });
2515
2740
  }
@@ -2964,8 +3189,8 @@ function BlockSettingsModal({
2964
3189
  setOpen(true);
2965
3190
  },
2966
3191
  disabled: !hasDraftSnapshot,
2967
- "aria-label": "Block settings",
2968
- title: "Block settings",
3192
+ "aria-label": "Customize block",
3193
+ title: "Customize block",
2969
3194
  children: /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" })
2970
3195
  }
2971
3196
  ) : null,
@@ -2989,7 +3214,7 @@ function BlockSettingsModal({
2989
3214
  !directSectionStylesModal ? /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(
2990
3215
  ScrollableDialogContent,
2991
3216
  {
2992
- title: "Block settings",
3217
+ title: "Customize block",
2993
3218
  footer: /* @__PURE__ */ jsx(Button, { type: "button", onClick: () => setOpen(false), children: "Done" }),
2994
3219
  children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultTab, className: "w-full", children: [
2995
3220
  /* @__PURE__ */ jsx(TabsList, { className: cn("mb-1"), children: visibleTabs.map((tab) => /* @__PURE__ */ jsx(TabsTrigger, { value: tab.id, children: tab.label }, tab.id)) }),
@@ -3024,6 +3249,124 @@ function useDraftContent(uiStore, blockId) {
3024
3249
  }, [uiStore, blockId]);
3025
3250
  return content;
3026
3251
  }
3252
+
3253
+ // ../editor-blocks/src/block-editor/blockHeaderActionPlan.ts
3254
+ function deriveBlockHeaderActionPlan({
3255
+ actionPresentation,
3256
+ blockId,
3257
+ settingsAvailable,
3258
+ featureCardIconActionsAvailable,
3259
+ featureCardIconsComplete,
3260
+ generatedIconSetActionsAvailable,
3261
+ hasStaleGeneratedIcons,
3262
+ featureCardIconDialogCardsCount,
3263
+ duplicateAvailable,
3264
+ duplicateDisabled,
3265
+ deleteAvailable,
3266
+ deleteDisabled
3267
+ }) {
3268
+ const customizeAction = deriveCustomizeAction({
3269
+ actionPresentation,
3270
+ blockId,
3271
+ settingsAvailable
3272
+ });
3273
+ const layout = actionPresentation.kind;
3274
+ if (layout === "inline") {
3275
+ return {
3276
+ layout,
3277
+ customizeAction,
3278
+ menuGroups: [],
3279
+ hasMenuActions: false
3280
+ };
3281
+ }
3282
+ const menuGroups = [];
3283
+ if (customizeAction) {
3284
+ menuGroups.push({
3285
+ id: "customize",
3286
+ actions: [customizeAction]
3287
+ });
3288
+ }
3289
+ const iconActions = deriveIconActions({
3290
+ featureCardIconActionsAvailable,
3291
+ featureCardIconsComplete,
3292
+ generatedIconSetActionsAvailable,
3293
+ hasStaleGeneratedIcons,
3294
+ featureCardIconDialogCardsCount
3295
+ });
3296
+ if (iconActions.length > 0) {
3297
+ menuGroups.push({
3298
+ id: "icons",
3299
+ actions: iconActions
3300
+ });
3301
+ }
3302
+ const structureActions = deriveStructureActions({
3303
+ duplicateAvailable,
3304
+ duplicateDisabled,
3305
+ deleteAvailable,
3306
+ deleteDisabled
3307
+ });
3308
+ if (structureActions.length > 0) {
3309
+ menuGroups.push({
3310
+ id: "structure",
3311
+ actions: structureActions
3312
+ });
3313
+ }
3314
+ return {
3315
+ layout,
3316
+ customizeAction,
3317
+ menuGroups,
3318
+ hasMenuActions: menuGroups.length > 0
3319
+ };
3320
+ }
3321
+ function deriveCustomizeAction({
3322
+ actionPresentation,
3323
+ blockId,
3324
+ settingsAvailable
3325
+ }) {
3326
+ if (actionPresentation.kind === "inline") {
3327
+ return settingsAvailable ? { kind: "customize", presentation: "modal" } : null;
3328
+ }
3329
+ switch (actionPresentation.customize.kind) {
3330
+ case "modal":
3331
+ return settingsAvailable ? { kind: "customize", presentation: "modal" } : null;
3332
+ case "external":
3333
+ return blockId ? { kind: "customize", presentation: "external", blockId } : null;
3334
+ default:
3335
+ return assertNever(actionPresentation.customize);
3336
+ }
3337
+ }
3338
+ function deriveIconActions({
3339
+ featureCardIconActionsAvailable,
3340
+ featureCardIconsComplete,
3341
+ generatedIconSetActionsAvailable,
3342
+ hasStaleGeneratedIcons,
3343
+ featureCardIconDialogCardsCount
3344
+ }) {
3345
+ if (!featureCardIconActionsAvailable) {
3346
+ return [];
3347
+ }
3348
+ return [
3349
+ { kind: "suggest-icons", disabled: featureCardIconsComplete },
3350
+ ...generatedIconSetActionsAvailable ? [
3351
+ {
3352
+ kind: "generated-icon-set",
3353
+ disabled: featureCardIconDialogCardsCount === 0,
3354
+ stale: hasStaleGeneratedIcons
3355
+ }
3356
+ ] : []
3357
+ ];
3358
+ }
3359
+ function deriveStructureActions({
3360
+ duplicateAvailable,
3361
+ duplicateDisabled,
3362
+ deleteAvailable,
3363
+ deleteDisabled
3364
+ }) {
3365
+ return [
3366
+ ...duplicateAvailable ? [{ kind: "duplicate", disabled: duplicateDisabled }] : [],
3367
+ ...deleteAvailable ? [{ kind: "delete", disabled: deleteDisabled }] : []
3368
+ ];
3369
+ }
3027
3370
  function BlockCardHeader({
3028
3371
  block,
3029
3372
  manifest,
@@ -3043,7 +3386,7 @@ function BlockCardHeader({
3043
3386
  theme,
3044
3387
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2 = false,
3045
3388
  reorderControlMode = "full",
3046
- actionControlMode = DEFAULT_BLOCK_ACTION_CONTROL_MODE,
3389
+ actionPresentation = DEFAULT_BLOCK_ACTION_PRESENTATION,
3047
3390
  currentStyleGuideVersionId,
3048
3391
  iconStyleGuideSummary,
3049
3392
  iconStyleGuideEditHref,
@@ -3055,7 +3398,6 @@ function BlockCardHeader({
3055
3398
  const [generateIconSetDialogOpen, setGenerateIconSetDialogOpen] = useState(false);
3056
3399
  const [settingsAvailable, setSettingsAvailable] = useState(true);
3057
3400
  const showMoveButtons = shouldShowMoveButtons(reorderControlMode);
3058
- const showActionsMenu = actionControlMode === "menu";
3059
3401
  const baselineContent = useDraftContent(
3060
3402
  uiStore,
3061
3403
  typeof block.id === "string" ? unsafeAsEditorBlockId(block.id) : null
@@ -3070,7 +3412,6 @@ function BlockCardHeader({
3070
3412
  const featureCardIconStatus = supportsIconActions ? getFeatureCardIconStatus(currentContent ?? block.draftContent ?? contentToUse) : null;
3071
3413
  const featureCardIconActionsAvailable = Boolean(blockId) && supportsIconActions && (featureCardIconStatus?.totalCards ?? 0) > 0;
3072
3414
  const featureCardIconsComplete = featureCardIconStatus !== null && featureCardIconStatus.totalCards > 0 && featureCardIconStatus.cardsWithIcons >= featureCardIconStatus.totalCards;
3073
- const featureCardSuggestionActionAvailable = featureCardIconActionsAvailable && !featureCardIconsComplete;
3074
3415
  const generatedIconSetActionsAvailable = featureCardIconActionsAvailable && Boolean(onGenerateIconSet);
3075
3416
  const featureCardIconContent = currentContent ?? block.draftContent ?? contentToUse;
3076
3417
  const featureCardIconDialogCards = supportsIconActions ? getFeatureCardIconDialogCards(featureCardIconContent) : [];
@@ -3096,8 +3437,22 @@ function BlockCardHeader({
3096
3437
  contentToUse,
3097
3438
  block.kind
3098
3439
  );
3099
- const hasMenuActions = settingsAvailable || featureCardSuggestionActionAvailable || generatedIconSetActionsAvailable || Boolean(onDuplicate) || Boolean(onDelete);
3100
- const settingsModal = /* @__PURE__ */ jsx(
3440
+ const actionPlan = deriveBlockHeaderActionPlan({
3441
+ actionPresentation,
3442
+ blockId,
3443
+ settingsAvailable,
3444
+ featureCardIconActionsAvailable,
3445
+ featureCardIconsComplete,
3446
+ generatedIconSetActionsAvailable,
3447
+ hasStaleGeneratedIcons,
3448
+ featureCardIconDialogCardsCount: featureCardIconDialogCards.length,
3449
+ duplicateAvailable: Boolean(onDuplicate),
3450
+ duplicateDisabled: isMoving || !block.id,
3451
+ deleteAvailable: Boolean(onDelete),
3452
+ deleteDisabled: !block.id
3453
+ });
3454
+ const showActionsMenu = actionPlan.layout === "menu";
3455
+ const settingsModal = actionPresentation.kind === "menu" && actionPresentation.customize.kind === "external" ? null : /* @__PURE__ */ jsx(
3101
3456
  BlockSettingsModal,
3102
3457
  {
3103
3458
  block,
@@ -3112,6 +3467,41 @@ function BlockCardHeader({
3112
3467
  onAvailabilityChange: showActionsMenu ? setSettingsAvailable : void 0
3113
3468
  }
3114
3469
  );
3470
+ function handleMenuAction(action) {
3471
+ switch (action.kind) {
3472
+ case "customize":
3473
+ if (action.presentation === "external") {
3474
+ if (actionPresentation.kind === "menu" && actionPresentation.customize.kind === "external") {
3475
+ actionPresentation.customize.onSelect(action.blockId);
3476
+ }
3477
+ return;
3478
+ }
3479
+ setSettingsDialogOpen(true);
3480
+ return;
3481
+ case "suggest-icons":
3482
+ if (!blockId || action.disabled) return;
3483
+ applyCuratedFeatureCardIconSuggestions({
3484
+ blockId,
3485
+ content: currentContent ?? block.draftContent ?? contentToUse,
3486
+ drafts: uiStore.getState().drafts
3487
+ });
3488
+ return;
3489
+ case "generated-icon-set":
3490
+ if (action.disabled) return;
3491
+ setGenerateIconSetDialogOpen(true);
3492
+ return;
3493
+ case "duplicate":
3494
+ if (block.id && onDuplicate && !action.disabled) {
3495
+ onDuplicate(block.id);
3496
+ }
3497
+ return;
3498
+ case "delete":
3499
+ if (!action.disabled) {
3500
+ setDeleteDialogOpen(true);
3501
+ }
3502
+ return;
3503
+ }
3504
+ }
3115
3505
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3116
3506
  /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-wrap items-start gap-1 px-2 py-2", "data-testid": "block-card-header", children: [
3117
3507
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
@@ -3195,78 +3585,28 @@ function BlockCardHeader({
3195
3585
  type: "button",
3196
3586
  variant: "ghost",
3197
3587
  size: "icon",
3198
- disabled: !hasMenuActions,
3588
+ disabled: !actionPlan.hasMenuActions,
3199
3589
  "aria-label": `More actions for ${displayTitle}`,
3200
3590
  title: "More actions",
3201
3591
  children: /* @__PURE__ */ jsx(EllipsisVertical, { className: "h-4 w-4" })
3202
3592
  }
3203
3593
  ) }),
3204
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [
3205
- settingsAvailable ? /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => setSettingsDialogOpen(true), children: [
3206
- /* @__PURE__ */ jsx(Settings, { className: "mr-2 h-4 w-4" }),
3207
- "Settings"
3208
- ] }) : null,
3209
- featureCardIconActionsAvailable ? /* @__PURE__ */ jsxs(Fragment, { children: [
3210
- settingsAvailable ? /* @__PURE__ */ jsx(DropdownMenuSeparator, {}) : null,
3211
- /* @__PURE__ */ jsxs(
3212
- DropdownMenuItem,
3213
- {
3214
- disabled: !blockId || featureCardIconsComplete,
3215
- onSelect: () => {
3216
- if (!blockId) return;
3217
- applyCuratedFeatureCardIconSuggestions({
3218
- blockId,
3219
- content: currentContent ?? block.draftContent ?? contentToUse,
3220
- drafts: uiStore.getState().drafts
3221
- });
3222
- },
3223
- children: [
3224
- /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" }),
3225
- "Suggest icons"
3226
- ]
3227
- }
3228
- ),
3229
- generatedIconSetActionsAvailable ? /* @__PURE__ */ jsxs(
3230
- DropdownMenuItem,
3231
- {
3232
- disabled: !blockId || featureCardIconDialogCards.length === 0,
3233
- onSelect: () => setGenerateIconSetDialogOpen(true),
3234
- children: [
3235
- hasStaleGeneratedIcons ? /* @__PURE__ */ jsx(RotateCcw, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "mr-2 h-4 w-4" }),
3236
- hasStaleGeneratedIcons ? "Regenerate icon set" : "Generate icon set"
3237
- ]
3238
- }
3239
- ) : null
3240
- ] }) : null,
3241
- (settingsAvailable || featureCardIconActionsAvailable) && (onDuplicate || onDelete) ? /* @__PURE__ */ jsx(DropdownMenuSeparator, {}) : null,
3242
- onDuplicate ? /* @__PURE__ */ jsxs(
3243
- DropdownMenuItem,
3244
- {
3245
- disabled: isMoving || !block.id,
3246
- onSelect: () => {
3247
- if (block.id) {
3248
- onDuplicate(block.id);
3249
- }
3250
- },
3251
- children: [
3252
- /* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }),
3253
- "Duplicate"
3254
- ]
3255
- }
3256
- ) : null,
3257
- onDelete ? /* @__PURE__ */ jsxs(
3594
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-44", children: actionPlan.menuGroups.map((group, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
3595
+ index > 0 ? /* @__PURE__ */ jsx(DropdownMenuSeparator, {}) : null,
3596
+ group.actions.map((action) => /* @__PURE__ */ jsxs(
3258
3597
  DropdownMenuItem,
3259
3598
  {
3260
- disabled: !block.id,
3261
- className: "text-destructive focus:text-destructive",
3262
- onSelect: () => setDeleteDialogOpen(true),
3599
+ disabled: "disabled" in action ? action.disabled : false,
3600
+ className: action.kind === "delete" ? "text-destructive focus:text-destructive" : void 0,
3601
+ onSelect: () => handleMenuAction(action),
3263
3602
  children: [
3264
- /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
3265
- "Delete"
3603
+ menuActionIcon(action),
3604
+ menuActionLabel(action)
3266
3605
  ]
3267
- }
3268
- ) : null
3269
- ] })
3606
+ },
3607
+ action.kind
3608
+ ))
3609
+ ] }, group.id)) })
3270
3610
  ] }),
3271
3611
  settingsModal
3272
3612
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -3350,6 +3690,38 @@ function BlockCardHeader({
3350
3690
  ) : null
3351
3691
  ] });
3352
3692
  }
3693
+ function menuActionIcon(action) {
3694
+ switch (action.kind) {
3695
+ case "customize":
3696
+ return /* @__PURE__ */ jsx(Settings, { className: "mr-2 h-4 w-4" });
3697
+ case "suggest-icons":
3698
+ return /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" });
3699
+ case "generated-icon-set":
3700
+ return action.stale ? /* @__PURE__ */ jsx(RotateCcw, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "mr-2 h-4 w-4" });
3701
+ case "duplicate":
3702
+ return /* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" });
3703
+ case "delete":
3704
+ return /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" });
3705
+ default:
3706
+ return assertNever(action);
3707
+ }
3708
+ }
3709
+ function menuActionLabel(action) {
3710
+ switch (action.kind) {
3711
+ case "customize":
3712
+ return "Customize";
3713
+ case "suggest-icons":
3714
+ return "Suggest icons";
3715
+ case "generated-icon-set":
3716
+ return action.stale ? "Regenerate icon set" : "Generate icon set";
3717
+ case "duplicate":
3718
+ return "Duplicate";
3719
+ case "delete":
3720
+ return "Delete";
3721
+ default:
3722
+ return assertNever(action);
3723
+ }
3724
+ }
3353
3725
  function BlockCard({
3354
3726
  siteId,
3355
3727
  block,
@@ -3375,7 +3747,7 @@ function BlockCard({
3375
3747
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2 = false,
3376
3748
  pageDesignBlock = null,
3377
3749
  reorderControlMode = DEFAULT_REORDER_CONTROL_MODE,
3378
- actionControlMode = DEFAULT_BLOCK_ACTION_CONTROL_MODE,
3750
+ actionPresentation = DEFAULT_BLOCK_ACTION_PRESENTATION,
3379
3751
  currentStyleGuideVersionId,
3380
3752
  iconStyleGuideSummary,
3381
3753
  iconStyleGuideEditHref,
@@ -3411,7 +3783,7 @@ function BlockCard({
3411
3783
  theme,
3412
3784
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2,
3413
3785
  reorderControlMode,
3414
- actionControlMode,
3786
+ actionPresentation,
3415
3787
  currentStyleGuideVersionId,
3416
3788
  iconStyleGuideSummary,
3417
3789
  iconStyleGuideEditHref,
@@ -3470,7 +3842,7 @@ var SortableBlockItem = memo(function SortableBlockItem2({
3470
3842
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2 = false,
3471
3843
  pageDesignBlock = null,
3472
3844
  reorderControlMode = DEFAULT_REORDER_CONTROL_MODE,
3473
- actionControlMode = DEFAULT_BLOCK_ACTION_CONTROL_MODE,
3845
+ actionPresentation = DEFAULT_BLOCK_ACTION_PRESENTATION,
3474
3846
  currentStyleGuideVersionId,
3475
3847
  iconStyleGuideSummary,
3476
3848
  iconStyleGuideEditHref,
@@ -3558,7 +3930,7 @@ var SortableBlockItem = memo(function SortableBlockItem2({
3558
3930
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2,
3559
3931
  pageDesignBlock,
3560
3932
  reorderControlMode,
3561
- actionControlMode,
3933
+ actionPresentation,
3562
3934
  currentStyleGuideVersionId,
3563
3935
  iconStyleGuideSummary,
3564
3936
  iconStyleGuideEditHref,
@@ -3591,7 +3963,7 @@ var BlockEditor = forwardRef(
3591
3963
  sdkConfig = null,
3592
3964
  customBlocks = [],
3593
3965
  reorderControlMode = DEFAULT_REORDER_CONTROL_MODE,
3594
- actionControlMode = DEFAULT_BLOCK_ACTION_CONTROL_MODE
3966
+ actionPresentation = DEFAULT_BLOCK_ACTION_PRESENTATION
3595
3967
  }, ref) {
3596
3968
  const [blockList, setBlockList] = useState(
3597
3969
  () => normalizeBlockListInitial(blocks)
@@ -3684,7 +4056,8 @@ var BlockEditor = forwardRef(
3684
4056
  bodyText: card.bodyText,
3685
4057
  semanticIconId: card.semanticIconId
3686
4058
  }))
3687
- }
4059
+ },
4060
+ options: input.signal ? { signal: input.signal } : void 0
3688
4061
  }));
3689
4062
  const assets = response.preview.assets.map((asset) => {
3690
4063
  const iconValue = parseBlockIconValue(asset.iconValue);
@@ -4048,7 +4421,7 @@ var BlockEditor = forwardRef(
4048
4421
  hideThemeV2RawStyleFields: hideThemeV2RawStyleFields2,
4049
4422
  pageDesignBlock: block.id ? pageDesignBlockById.get(block.id) ?? null : null,
4050
4423
  reorderControlMode,
4051
- actionControlMode,
4424
+ actionPresentation,
4052
4425
  currentStyleGuideVersionId: iconStyleGuide?.versionId ?? null,
4053
4426
  iconStyleGuideSummary: iconStyleGuide?.summary ?? null,
4054
4427
  iconStyleGuideEditHref: iconStyleGuide?.editHref ?? null,
@@ -4101,6 +4474,22 @@ var BlockEditor = forwardRef(
4101
4474
  ] }) });
4102
4475
  }
4103
4476
  );
4477
+ var LinkWidgetContext = createContext(null);
4478
+ function LinkWidgetProvider({
4479
+ children,
4480
+ siteId,
4481
+ adapters,
4482
+ initialItems
4483
+ }) {
4484
+ return /* @__PURE__ */ jsx(LinkWidgetContext.Provider, { value: { siteId, adapters, initialItems }, children });
4485
+ }
4486
+ function useLinkWidgetContext() {
4487
+ const ctx = useContext(LinkWidgetContext);
4488
+ if (!ctx) {
4489
+ return {};
4490
+ }
4491
+ return ctx;
4492
+ }
4104
4493
 
4105
4494
  // ../editor-blocks/src/widgets/link/utils.ts
4106
4495
  function toInternalLinkValue(item) {
@@ -4530,22 +4919,6 @@ function LinkWidget({
4530
4919
  )
4531
4920
  ] }) }) });
4532
4921
  }
4533
- var LinkWidgetContext = createContext(null);
4534
- function LinkWidgetProvider({
4535
- children,
4536
- siteId,
4537
- adapters,
4538
- initialItems
4539
- }) {
4540
- return /* @__PURE__ */ jsx(LinkWidgetContext.Provider, { value: { siteId, adapters, initialItems }, children });
4541
- }
4542
- function useLinkWidgetContext() {
4543
- const ctx = useContext(LinkWidgetContext);
4544
- if (!ctx) {
4545
- return {};
4546
- }
4547
- return ctx;
4548
- }
4549
4922
  function LinkField({ path, backend, config }) {
4550
4923
  const { value, setValue } = useField(path, backend);
4551
4924
  const { siteId, adapters, initialItems } = useLinkWidgetContext();
@@ -5375,28 +5748,87 @@ function filenameExtension(filename) {
5375
5748
  if (dotIndex < 0 || dotIndex === filename.length - 1) return null;
5376
5749
  return filename.slice(dotIndex + 1).trim().toLowerCase() || null;
5377
5750
  }
5378
- createContext(null);
5379
- var EntryPickerWidgetContext = createContext(null);
5380
- function EntryPickerWidgetProvider({
5381
- siteId,
5382
- adapter = null,
5383
- children
5384
- }) {
5385
- return /* @__PURE__ */ jsx(EntryPickerWidgetContext.Provider, { value: { siteId, adapter }, children });
5386
- }
5387
- function useEntryPickerWidgetContext() {
5388
- const ctx = useContext(EntryPickerWidgetContext);
5389
- if (!ctx) {
5390
- throw new Error("useEntryPickerWidgetContext must be used within EntryPickerWidgetProvider");
5391
- }
5392
- return ctx;
5393
- }
5394
-
5395
- // ../editor-blocks/src/widgets/entry-picker/utils.ts
5396
- function isNonEmptyString(value) {
5397
- return typeof value === "string" && value.trim().length > 0;
5398
- }
5399
- function findItemForStoredValue(storedValue, items) {
5751
+ var ButtonWidgetContext = createContext(null);
5752
+ function ButtonWidgetProvider({ buttonSystem, children }) {
5753
+ const value = useMemo(() => {
5754
+ const variants = buttonSystem ? buttonSystem.variants.filter((v) => v.enabled).map((v) => ({
5755
+ id: v.id,
5756
+ name: v.name,
5757
+ priority: v.priority
5758
+ })).sort((a, b) => a.priority - b.priority) : [];
5759
+ const defaultVariant = buttonSystem?.variants.find((v) => v.priority === 1)?.id ?? null;
5760
+ return {
5761
+ variants,
5762
+ defaultVariant,
5763
+ buttonSystem
5764
+ };
5765
+ }, [buttonSystem]);
5766
+ return /* @__PURE__ */ jsx(ButtonWidgetContext.Provider, { value, children });
5767
+ }
5768
+ function useButtonWidgetContext() {
5769
+ const context = useContext(ButtonWidgetContext);
5770
+ if (!context) {
5771
+ throw new Error("useButtonWidgetContext must be used within ButtonWidgetProvider");
5772
+ }
5773
+ return context;
5774
+ }
5775
+ var DEFAULT_VARIANTS = [
5776
+ { id: "primary", name: "Primary", priority: 1 },
5777
+ { id: "secondary", name: "Secondary", priority: 2 },
5778
+ { id: "outline", name: "Outline", priority: 3 }
5779
+ ];
5780
+ function ButtonVariantField({ path, backend, config }) {
5781
+ const { value, error, setValue } = useField(path, backend);
5782
+ const buttonContext = useButtonWidgetContext();
5783
+ const variants = buttonContext.variants.length > 0 ? buttonContext.variants : DEFAULT_VARIANTS;
5784
+ const options = variants.map((v) => ({ value: v.id, label: v.name }));
5785
+ const isUnknownVariant = value && !options.some((opt) => opt.value === value);
5786
+ if (isUnknownVariant && true) {
5787
+ console.warn(
5788
+ `[ButtonVariantField] Unknown button variant "${value}" at path "${path}". Available variants: ${options.map((o) => o.value).join(", ")}. The button may not render with expected styling.`
5789
+ );
5790
+ }
5791
+ return /* @__PURE__ */ jsx(
5792
+ FieldFrame,
5793
+ {
5794
+ label: config.label,
5795
+ description: config.description,
5796
+ error: error?.message,
5797
+ children: () => /* @__PURE__ */ jsxs(
5798
+ Select,
5799
+ {
5800
+ value: typeof value === "string" && value !== "" ? value : void 0,
5801
+ onValueChange: setValue,
5802
+ children: [
5803
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: `Select ${config.label.toLowerCase()}` }) }),
5804
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
5805
+ ]
5806
+ }
5807
+ )
5808
+ }
5809
+ );
5810
+ }
5811
+ var EntryPickerWidgetContext = createContext(null);
5812
+ function EntryPickerWidgetProvider({
5813
+ siteId,
5814
+ adapter = null,
5815
+ children
5816
+ }) {
5817
+ return /* @__PURE__ */ jsx(EntryPickerWidgetContext.Provider, { value: { siteId, adapter }, children });
5818
+ }
5819
+ function useEntryPickerWidgetContext() {
5820
+ const ctx = useContext(EntryPickerWidgetContext);
5821
+ if (!ctx) {
5822
+ throw new Error("useEntryPickerWidgetContext must be used within EntryPickerWidgetProvider");
5823
+ }
5824
+ return ctx;
5825
+ }
5826
+
5827
+ // ../editor-blocks/src/widgets/entry-picker/utils.ts
5828
+ function isNonEmptyString(value) {
5829
+ return typeof value === "string" && value.trim().length > 0;
5830
+ }
5831
+ function findItemForStoredValue(storedValue, items) {
5400
5832
  if (!isNonEmptyString(storedValue)) return void 0;
5401
5833
  return items.find((item) => item.value === storedValue) ?? items.find((item) => item.id === storedValue) ?? items.find((item) => item.identifier === storedValue) ?? items.find((item) => item.slug === storedValue);
5402
5834
  }
@@ -5534,2482 +5966,491 @@ function IconPickerField({ path, backend, config }) {
5534
5966
  }
5535
5967
  ) });
5536
5968
  }
5537
-
5538
- // ../editor-blocks/src/widgets/entry-picker/loaders.ts
5539
- function getStatusBadgeVariant(status) {
5540
- switch (status) {
5541
- case "published":
5542
- return "default";
5543
- case "draft":
5544
- return "secondary";
5545
- case "scheduled":
5546
- return "outline";
5547
- case "archived":
5548
- return "destructive";
5549
- default:
5550
- return "secondary";
5551
- }
5552
- }
5553
- function mapEntryPickerItems(entries, valueFrom) {
5554
- return entries.map((entry) => ({
5555
- value: valueFrom === "identifier" ? entry.identifier ?? entry.id : valueFrom === "slug" ? entry.slug ?? entry.id : entry.id,
5556
- label: entry.draftTitle || entry.title || entry.slug || entry.id,
5557
- id: entry.id,
5558
- identifier: entry.identifier ?? null,
5559
- slug: entry.slug ?? null,
5560
- badge: {
5561
- label: entry.status,
5562
- variant: getStatusBadgeVariant(entry.status)
5563
- }
5564
- }));
5565
- }
5566
- async function loadEntryPickerItems({
5567
- apiClient,
5969
+ var SiteOptionWidgetContext = createContext(null);
5970
+ function SiteOptionWidgetProvider({
5568
5971
  siteId,
5569
- contentType,
5570
- valueFrom
5571
- }) {
5572
- const result = await apiClient({
5573
- endpoint: "listContentEntries",
5574
- params: { siteId, type: contentType }
5575
- });
5576
- const { entries } = unwrapResponse(result);
5577
- return mapEntryPickerItems(entries, valueFrom);
5578
- }
5579
-
5580
- // ../editor-blocks/src/widgets/link/loaders.ts
5581
- async function loadRoutableContentItems({
5582
- apiClient,
5583
- siteId
5972
+ adapters,
5973
+ children
5584
5974
  }) {
5585
- const result = await apiClient({
5586
- endpoint: "getRoutableContent",
5587
- params: { siteId }
5588
- });
5589
- const payload = unwrapResponse(result);
5590
- return Array.isArray(payload?.items) ? payload.items : [];
5975
+ return /* @__PURE__ */ jsx(SiteOptionWidgetContext.Provider, { value: { siteId, adapters }, children });
5591
5976
  }
5592
-
5593
- // ../editor-blocks/src/widgets/reference/dynamicAdapters.ts
5594
- function createDynamicReferenceAdapters(staticAdapters, createDynamicContentAdapter) {
5595
- return new Proxy(staticAdapters, {
5596
- get(target, prop) {
5597
- if (typeof prop !== "string") {
5598
- return void 0;
5599
- }
5600
- if (prop in target) {
5601
- return target[prop];
5602
- }
5603
- const parsed = parseReferenceKind(prop);
5604
- if (parsed.kind === "dynamicContent") {
5605
- return createDynamicContentAdapter(parsed.contentTypeKey);
5606
- }
5607
- return void 0;
5608
- }
5609
- });
5977
+ function useSiteOptionWidgetContext() {
5978
+ const ctx = useContext(SiteOptionWidgetContext);
5979
+ if (!ctx) {
5980
+ throw new Error("useSiteOptionWidgetContext must be used within SiteOptionWidgetProvider");
5981
+ }
5982
+ return ctx;
5610
5983
  }
5611
5984
 
5612
- // ../editor-blocks/src/widgets/reference/loaders.ts
5613
- function defineReferenceLoader(referenceKind, loader) {
5614
- return Object.assign(loader, { referenceKind });
5615
- }
5616
- function formatStatusBadge(status) {
5617
- if (status === "published") {
5618
- return { label: "Published", variant: "default" };
5985
+ // ../editor-blocks/src/widgets/site-option-picker/utils.ts
5986
+ function normalizeSiteOptionMultiValue(value) {
5987
+ if (Array.isArray(value)) {
5988
+ return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
5619
5989
  }
5620
- if (status === "scheduled") {
5621
- return { label: "Scheduled", variant: "secondary" };
5990
+ if (typeof value === "string") {
5991
+ const trimmed = value.trim();
5992
+ return trimmed ? [trimmed] : [];
5622
5993
  }
5623
- if (status === "archived") {
5624
- return { label: "Archived", variant: "outline" };
5994
+ return [];
5995
+ }
5996
+ function getSiteOptionSource(config) {
5997
+ const raw = (config.ui ?? {}).siteOptionSource;
5998
+ if (typeof raw !== "string") {
5999
+ return null;
5625
6000
  }
5626
- return { label: "Draft", variant: "outline" };
6001
+ const trimmed = raw.trim();
6002
+ return siteOptionSources.includes(trimmed) ? trimmed : null;
5627
6003
  }
5628
- function hasSlug(entry) {
5629
- return entry.slug !== null;
6004
+ function getSearchThreshold(config) {
6005
+ return normalizePickerSearchThreshold((config.ui ?? {}).searchThreshold);
5630
6006
  }
5631
- var loadPostReferenceItems = defineReferenceLoader("post", async ({ apiClient, siteId }) => {
5632
- const result = await apiClient({
5633
- endpoint: "listContentEntries",
5634
- params: { siteId, type: "post" }
5635
- });
5636
- const { entries } = unwrapResponse(result);
5637
- return entries.filter(hasSlug).filter((entry) => entry.status === "published").map((entry) => ({
5638
- value: entry.slug,
5639
- label: entry.title || entry.slug || entry.id,
5640
- badge: formatStatusBadge(entry.status)
5641
- }));
5642
- });
5643
- var loadFormReferenceItems = defineReferenceLoader("form", async ({ apiClient, siteId }) => {
5644
- const result = await apiClient({
5645
- endpoint: "listForms",
5646
- params: { siteId }
5647
- });
5648
- const { forms } = unwrapResponse(result);
5649
- return forms.map((form) => ({
5650
- value: form.slug,
5651
- label: form.name
5652
- }));
5653
- });
5654
- var loadBookingFormReferenceItems = defineReferenceLoader("bookingForm", async ({ apiClient, siteId }) => {
5655
- const result = await apiClient({
5656
- endpoint: "listBookingForms",
5657
- params: { siteId }
6007
+ function SiteOptionPickerField({
6008
+ path,
6009
+ backend,
6010
+ config
6011
+ }) {
6012
+ const { value, error, setValue } = useField(path, backend);
6013
+ const { siteId, adapters } = useSiteOptionWidgetContext();
6014
+ const source = getSiteOptionSource(config);
6015
+ const searchThreshold = getSearchThreshold(config);
6016
+ const adapter = useMemo(() => {
6017
+ if (!adapters || !source) return null;
6018
+ return adapters[source] ?? null;
6019
+ }, [adapters, source]);
6020
+ const labels = useMemo(() => {
6021
+ const defaults = {
6022
+ loading: "Loading options...",
6023
+ select: config.multiple ? "Select options" : "Select an option",
6024
+ empty: "No options available",
6025
+ emptySelection: config.multiple ? "No selections yet." : "No selection yet."
6026
+ };
6027
+ return { ...defaults, ...adapter?.labels ?? {} };
6028
+ }, [adapter, config.multiple]);
6029
+ const loadItems = useCallback(async () => {
6030
+ if (!adapter || !siteId || !source) {
6031
+ return [];
6032
+ }
6033
+ return adapter.loadItems({ siteId, source });
6034
+ }, [adapter, siteId, source]);
6035
+ const { options: items, isLoading, errorMessage: loadError } = useAsyncPickerOptions({
6036
+ enabled: Boolean(adapter && siteId && source),
6037
+ loadOptions: loadItems
5658
6038
  });
5659
- const { forms } = unwrapResponse(result);
5660
- return forms.map((form) => ({
5661
- value: form.id,
5662
- label: form.name
5663
- }));
5664
- });
5665
- var loadAppointmentServiceReferenceItems = defineReferenceLoader(
5666
- "appointmentService",
5667
- async ({ apiClient, siteId }) => {
5668
- const result = await apiClient({
5669
- endpoint: "getAppointmentServicesReference",
5670
- params: { siteId }
5671
- });
5672
- const { options } = unwrapResponse(result);
5673
- return options.map((option) => ({
5674
- value: option.id,
5675
- label: option.label
5676
- }));
6039
+ if (!adapter || !siteId || !source) {
6040
+ return /* @__PURE__ */ jsx(FieldFrame, { label: config.label, description: config.description, error: error?.message, children: () => /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed bg-muted/10 p-4 text-sm text-muted-foreground", children: "This field type is not supported yet." }) });
5677
6041
  }
5678
- );
5679
- var loadAppointmentResourceReferenceItems = defineReferenceLoader(
5680
- "appointmentResource",
5681
- async ({ apiClient, siteId }) => {
5682
- const result = await apiClient({
5683
- endpoint: "getAppointmentResourcesReference",
5684
- params: { siteId }
5685
- });
5686
- const { options } = unwrapResponse(result);
5687
- return options.map((option) => ({
5688
- value: option.id,
5689
- label: option.label
5690
- }));
6042
+ const manageLink = adapter.renderManageLink?.({ siteId, source });
6043
+ const showSearch = shouldShowPickerSearch(items.length, searchThreshold);
6044
+ const helperText = loadError ? loadError : isLoading ? labels.loading : void 0;
6045
+ if (config.multiple) {
6046
+ const selectedValues = normalizeSiteOptionMultiValue(value);
6047
+ return /* @__PURE__ */ jsx(
6048
+ FieldFrame,
6049
+ {
6050
+ label: config.label,
6051
+ description: config.description,
6052
+ error: error?.message,
6053
+ hint: helperText ?? labels.emptySelection,
6054
+ children: () => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6055
+ /* @__PURE__ */ jsx(
6056
+ CommandMultiSelect,
6057
+ {
6058
+ items: items.map((item) => ({
6059
+ id: item.value,
6060
+ label: item.label,
6061
+ subtitle: item.subtitle
6062
+ })),
6063
+ values: selectedValues,
6064
+ onValuesChange: (nextValues) => setValue(nextValues),
6065
+ placeholder: labels.select,
6066
+ emptySelectionLabel: labels.emptySelection,
6067
+ emptyLabel: labels.empty,
6068
+ isLoading,
6069
+ error: loadError,
6070
+ selectedSummary: (selectedItems) => selectedItems.length === 1 ? selectedItems[0]?.label ?? "1 selected" : `${selectedItems.length} selected`,
6071
+ showSearch,
6072
+ searchPlaceholder: `Search ${config.label.toLowerCase()}...`
6073
+ }
6074
+ ),
6075
+ manageLink ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: manageLink }) : null
6076
+ ] })
6077
+ }
6078
+ );
5691
6079
  }
5692
- );
5693
- var loadPageReferenceItems = defineReferenceLoader("page", async ({ apiClient, siteId }) => {
5694
- const result = await apiClient({
5695
- endpoint: "getRoutableContent",
5696
- params: { siteId }
5697
- });
5698
- const { items } = unwrapResponse(result);
5699
- return items.filter((item) => item.entityType === "page").map((page) => ({
5700
- value: page.entityId,
5701
- label: page.title || "Untitled"
5702
- }));
5703
- });
5704
- var loadNewsletterListReferenceItems = defineReferenceLoader(
5705
- "newsletterList",
5706
- async ({ apiClient, siteId }) => {
5707
- const result = await apiClient({
5708
- endpoint: "listNewsletterLists",
5709
- params: { siteId }
5710
- });
5711
- const { lists } = unwrapResponse(result);
5712
- return lists.map((list) => ({
5713
- value: list.id,
5714
- label: list.name
5715
- }));
6080
+ const currentValue = typeof value === "string" ? value : "";
6081
+ const selectionLabel = resolvePickerSelectionLabel(
6082
+ currentValue || null,
6083
+ items,
6084
+ ({ value: value2, options }) => adapter.formatSelectionLabel?.({ value: value2, items: options }) ?? value2
6085
+ );
6086
+ return /* @__PURE__ */ jsx(
6087
+ FieldFrame,
6088
+ {
6089
+ label: config.label,
6090
+ description: config.description,
6091
+ error: error?.message,
6092
+ hint: helperText ?? (selectionLabel ? `Selected: ${selectionLabel}` : labels.emptySelection),
6093
+ children: ({ id, messageId }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6094
+ /* @__PURE__ */ jsx(
6095
+ Combobox,
6096
+ {
6097
+ id,
6098
+ "aria-describedby": messageId || void 0,
6099
+ items: items.map((item) => ({
6100
+ id: item.value,
6101
+ label: item.label,
6102
+ item
6103
+ })),
6104
+ value: currentValue || void 0,
6105
+ onSelect: (entry) => setValue(entry?.id ?? ""),
6106
+ placeholder: isLoading ? labels.loading : labels.select,
6107
+ emptyMessage: labels.empty,
6108
+ clearLabel: "Clear",
6109
+ search: showSearch,
6110
+ searchInputAriaLabel: `Search ${config.label}`,
6111
+ disabled: Boolean(isLoading || loadError)
6112
+ }
6113
+ ),
6114
+ manageLink ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: manageLink }) : null
6115
+ ] })
6116
+ }
6117
+ );
6118
+ }
6119
+ createContext(null);
6120
+
6121
+ // ../editor-blocks/src/widgets/site-option-picker/loaders.ts
6122
+ var siteOptionWidgetLabels = {
6123
+ eventCategories: {
6124
+ loading: "Loading categories...",
6125
+ select: "Select categories",
6126
+ empty: "No categories yet",
6127
+ emptySelection: "Choose which event categories to include."
6128
+ },
6129
+ venues: {
6130
+ loading: "Loading venues...",
6131
+ select: "Select a venue",
6132
+ empty: "No venues yet",
6133
+ emptySelection: "Choose a venue to restrict this block."
6134
+ },
6135
+ staffMembers: {
6136
+ loading: "Loading staff...",
6137
+ select: "Select a staff member",
6138
+ empty: "No staff yet",
6139
+ emptySelection: "Choose a staff member to restrict this block."
5716
6140
  }
5717
- );
5718
- async function loadDynamicContentReferenceItems({
6141
+ };
6142
+ function mapEventCategoriesToSiteOptionItems(payload) {
6143
+ return payload.categories.map((category) => ({
6144
+ value: category.id,
6145
+ label: category.name,
6146
+ subtitle: category.identifier
6147
+ }));
6148
+ }
6149
+ function mapVenuesToSiteOptionItems(payload) {
6150
+ return payload.map((venue) => ({
6151
+ value: venue.id,
6152
+ label: venue.title
6153
+ }));
6154
+ }
6155
+ function mapStaffMembersToSiteOptionItems(payload) {
6156
+ return payload.resources.map((staffMember) => ({
6157
+ value: staffMember.id,
6158
+ label: staffMember.displayName,
6159
+ subtitle: staffMember.bio ?? void 0
6160
+ }));
6161
+ }
6162
+ async function loadSiteOptionItemsFromApi({
5719
6163
  apiClient,
5720
6164
  siteId,
5721
- contentTypeKey
6165
+ source
5722
6166
  }) {
5723
- const result = await apiClient({
5724
- endpoint: "listContentEntries",
5725
- params: { siteId, type: contentTypeKey }
5726
- });
5727
- const { entries } = unwrapResponse(result);
5728
- return entries.filter((entry) => entry.status === "published").map((entry) => ({
5729
- value: entry.id,
5730
- label: entry.title || entry.slug || entry.id,
5731
- badge: { label: "Published", variant: "default" }
5732
- }));
5733
- }
5734
- var referenceLoaders = {
5735
- post: loadPostReferenceItems,
5736
- form: loadFormReferenceItems,
5737
- bookingForm: loadBookingFormReferenceItems,
5738
- appointmentService: loadAppointmentServiceReferenceItems,
5739
- appointmentResource: loadAppointmentResourceReferenceItems,
5740
- page: loadPageReferenceItems,
5741
- newsletterList: loadNewsletterListReferenceItems
5742
- };
5743
-
5744
- // ../preview-next/src/client/widgets/widgetAdapters.ts
5745
- var routableContentCache = /* @__PURE__ */ new Map();
5746
- var entryPickerCache = /* @__PURE__ */ new Map();
5747
- var referenceItemsCache = /* @__PURE__ */ new Map();
5748
- var dynamicReferenceItemsCache = /* @__PURE__ */ new Map();
5749
- var MAX_CACHE_ENTRIES = 50;
5750
- function setBoundedCache(map, key, value) {
5751
- if (!map.has(key) && map.size >= MAX_CACHE_ENTRIES) {
5752
- const firstKey = map.keys().next().value;
5753
- if (firstKey !== void 0) {
5754
- map.delete(firstKey);
6167
+ switch (source) {
6168
+ case "eventCategories": {
6169
+ const result = await apiClient({
6170
+ endpoint: "listEventCategories",
6171
+ params: { siteId }
6172
+ });
6173
+ return mapEventCategoriesToSiteOptionItems(unwrapResponse(result));
6174
+ }
6175
+ case "venues": {
6176
+ const result = await apiClient({
6177
+ endpoint: "listVenues",
6178
+ params: { siteId }
6179
+ });
6180
+ return mapVenuesToSiteOptionItems(unwrapResponse(result));
6181
+ }
6182
+ case "staffMembers": {
6183
+ const result = await apiClient({
6184
+ endpoint: "listAppointmentResources",
6185
+ params: { siteId }
6186
+ });
6187
+ return mapStaffMembersToSiteOptionItems(unwrapResponse(result));
5755
6188
  }
6189
+ default:
6190
+ return assertNever(source);
5756
6191
  }
5757
- map.set(key, value);
5758
6192
  }
5759
- function createCachedDynamicContentReferenceAdapter(contentTypeKey, previewClient, fallbackSiteId) {
6193
+ function createSiteOptionApiLoader(apiClient) {
6194
+ return (args) => loadSiteOptionItemsFromApi({ apiClient, ...args });
6195
+ }
6196
+ function createSiteOptionWidgetAdapters({
6197
+ loadItems,
6198
+ mapLoadError,
6199
+ renderManageLink
6200
+ }) {
5760
6201
  return {
5761
- labels: {
5762
- loading: "Loading entries\u2026",
5763
- select: "Select an entry",
5764
- empty: "No entries found",
5765
- emptySelection: "Select an entry to continue."
5766
- },
5767
- loadItems: async ({ siteId }) => {
5768
- const resolvedSiteId = siteId || fallbackSiteId;
5769
- if (!resolvedSiteId) {
5770
- return [];
5771
- }
5772
- const cacheKey = `${resolvedSiteId}:content:${contentTypeKey}`;
5773
- const cached = dynamicReferenceItemsCache.get(cacheKey);
5774
- if (cached) return cached;
5775
- const promise = (async () => {
5776
- try {
5777
- return await loadDynamicContentReferenceItems({
5778
- apiClient: previewClient,
5779
- siteId: resolvedSiteId,
5780
- contentTypeKey
5781
- });
5782
- } catch (error) {
5783
- dynamicReferenceItemsCache.delete(cacheKey);
5784
- throw error;
5785
- }
5786
- })();
5787
- setBoundedCache(dynamicReferenceItemsCache, cacheKey, promise);
5788
- return promise;
5789
- }
6202
+ eventCategories: createSiteOptionWidgetAdapter("eventCategories", loadItems, mapLoadError, renderManageLink),
6203
+ venues: createSiteOptionWidgetAdapter("venues", loadItems, mapLoadError, renderManageLink),
6204
+ staffMembers: createSiteOptionWidgetAdapter("staffMembers", loadItems, mapLoadError, renderManageLink)
5790
6205
  };
5791
6206
  }
5792
- function createCachedReferenceAdapterLoader(kind, loader, previewClient, fallbackSiteId) {
5793
- return async ({ siteId }) => {
5794
- const resolvedSiteId = siteId || fallbackSiteId;
5795
- if (!resolvedSiteId) {
5796
- return [];
5797
- }
5798
- const cacheKey = `${kind}:${resolvedSiteId}`;
5799
- const cached = referenceItemsCache.get(cacheKey);
5800
- if (cached) return cached;
5801
- const promise = (async () => {
6207
+ function createSiteOptionWidgetAdapter(source, loadItems, mapLoadError, renderManageLink) {
6208
+ return {
6209
+ labels: siteOptionWidgetLabels[source],
6210
+ renderManageLink: renderManageLink?.[source],
6211
+ loadItems: async ({ siteId }) => {
6212
+ if (!siteId) return [];
6213
+ const args = { siteId, source };
5802
6214
  try {
5803
- return await loader({ apiClient: previewClient, siteId: resolvedSiteId });
6215
+ return await loadItems(args);
5804
6216
  } catch (error) {
5805
- referenceItemsCache.delete(cacheKey);
5806
- throw error;
6217
+ throw mapLoadError ? mapLoadError(error, args) : error;
5807
6218
  }
5808
- })();
5809
- setBoundedCache(referenceItemsCache, cacheKey, promise);
5810
- return promise;
6219
+ }
5811
6220
  };
5812
6221
  }
5813
- function createPreviewLinkAdapters(previewClient) {
5814
- return {
5815
- fetchRoutableContent: async (currentSiteId) => {
5816
- const cached = routableContentCache.get(currentSiteId);
5817
- if (cached) return cached;
5818
- const promise = (async () => {
5819
- try {
5820
- return await loadRoutableContentItems({ apiClient: previewClient, siteId: currentSiteId });
5821
- } catch (error) {
5822
- routableContentCache.delete(currentSiteId);
5823
- throw error;
5824
- }
5825
- })();
5826
- setBoundedCache(routableContentCache, currentSiteId, promise);
5827
- return promise;
6222
+
6223
+ // ../preview-next/src/client/blocks/previewCustomizeControlPlan.ts
6224
+ var ABSOLUTE_MAX_QUICK_FIELDS = 3;
6225
+ var QUICK_FIELD_PRIORITY_BY_BLOCK_KIND = {
6226
+ "block.testimonials": [
6227
+ "avatarLayout",
6228
+ "gridColumns",
6229
+ "slidesToShow",
6230
+ "maxEntries",
6231
+ "autoplay"
6232
+ ],
6233
+ "block.image-gallery": [
6234
+ "aspectRatio",
6235
+ "slidesToShow",
6236
+ "fullBleed",
6237
+ "marqueeSpeed",
6238
+ "marqueePauseOnHover"
6239
+ ],
6240
+ "block.event-calendar": [
6241
+ "listLayout",
6242
+ "eventsPerPage",
6243
+ "showFilters",
6244
+ "calendarView",
6245
+ "eventSurface"
6246
+ ],
6247
+ "block.event-listing": [
6248
+ "layout",
6249
+ "columns",
6250
+ "eventsPerPage",
6251
+ "showFilters",
6252
+ "eventSurface"
6253
+ ]
6254
+ };
6255
+ function createPreviewCustomizeControlPlan({
6256
+ blockKind,
6257
+ fields,
6258
+ content,
6259
+ maxQuickFields = ABSOLUTE_MAX_QUICK_FIELDS
6260
+ }) {
6261
+ const valueReader = createVisibilityValueReader(content);
6262
+ const quickPriority = QUICK_FIELD_PRIORITY_BY_BLOCK_KIND[blockKind] ?? [];
6263
+ const quickPriorityIndex = new Map(
6264
+ quickPriority.map((fieldId, index) => [fieldId, index])
6265
+ );
6266
+ const maxQuick = Math.max(0, Math.min(ABSOLUTE_MAX_QUICK_FIELDS, maxQuickFields));
6267
+ const visibleControls = [];
6268
+ const hiddenControls = [];
6269
+ for (const field of fields) {
6270
+ if (isThemeV2NormalEditorRawStyleFieldName(field.id)) {
6271
+ hiddenControls.push({
6272
+ fieldId: field.id,
6273
+ reason: "theme-v2-raw-style-field"
6274
+ });
6275
+ continue;
6276
+ }
6277
+ if (isHeaderField(field)) {
6278
+ hiddenControls.push({ fieldId: field.id, reason: "header-field" });
6279
+ continue;
6280
+ }
6281
+ const intent = getFieldIntent(field);
6282
+ if (intent !== "display" && intent !== "behavior") {
6283
+ hiddenControls.push({
6284
+ fieldId: field.id,
6285
+ reason: "unsupported-intent"
6286
+ });
6287
+ continue;
6288
+ }
6289
+ if (!evaluateVisibility(field, valueReader, field.id)) {
6290
+ hiddenControls.push({ fieldId: field.id, reason: "not-visible" });
6291
+ continue;
5828
6292
  }
6293
+ visibleControls.push({
6294
+ kind: "manifest-field",
6295
+ field,
6296
+ placement: "panel",
6297
+ intent
6298
+ });
6299
+ }
6300
+ const quickFieldIds = new Set(
6301
+ visibleControls.filter((control) => quickPriorityIndex.has(control.field.id)).sort(
6302
+ (left, right) => (quickPriorityIndex.get(left.field.id) ?? Number.MAX_SAFE_INTEGER) - (quickPriorityIndex.get(right.field.id) ?? Number.MAX_SAFE_INTEGER)
6303
+ ).slice(0, maxQuick).map((control) => control.field.id)
6304
+ );
6305
+ return {
6306
+ quickControls: visibleControls.filter((control) => quickFieldIds.has(control.field.id)).map((control) => ({ ...control, placement: "quick" })),
6307
+ panelControls: visibleControls.filter(
6308
+ (control) => !quickFieldIds.has(control.field.id)
6309
+ ),
6310
+ hiddenControls
5829
6311
  };
5830
6312
  }
5831
- function createPreviewEntryPickerAdapter(previewClient) {
6313
+ function createVisibilityValueReader(content) {
5832
6314
  return {
5833
- loadItems: async ({ siteId, contentType, valueFrom }) => {
5834
- if (!siteId || !contentType) return [];
5835
- const cacheKey = `${siteId}:${contentType}:${valueFrom ?? "id"}`;
5836
- const cached = entryPickerCache.get(cacheKey);
5837
- if (cached) return cached;
5838
- const request = (async () => {
5839
- try {
5840
- return await loadEntryPickerItems({
5841
- apiClient: previewClient,
5842
- siteId,
5843
- contentType,
5844
- valueFrom
5845
- });
5846
- } catch (error) {
5847
- entryPickerCache.delete(cacheKey);
5848
- throw error;
5849
- }
5850
- })();
5851
- setBoundedCache(entryPickerCache, cacheKey, request);
5852
- return request;
5853
- },
5854
- labels: {
5855
- loading: "Loading entries...",
5856
- select: "Select an entry",
5857
- empty: "No entries found",
5858
- emptySelection: "Select an entry to embed.",
5859
- noContentType: "Select a content type first."
5860
- }
6315
+ getValue: (path) => getValueAtPath(content, path)
5861
6316
  };
5862
6317
  }
5863
- var previewReferenceChrome = {
5864
- post: {
5865
- labels: {
5866
- loading: "Loading posts\u2026",
5867
- select: "Select a post",
5868
- empty: "No posts found",
5869
- emptySelection: "Select a post to continue."
5870
- }
5871
- },
5872
- form: {
5873
- labels: {
5874
- loading: "Loading forms\u2026",
5875
- select: "Select a form",
5876
- empty: "No forms found",
5877
- emptySelection: "Select a form to continue."
5878
- }
5879
- },
5880
- bookingForm: {
5881
- labels: {
5882
- loading: "Loading booking forms\u2026",
5883
- select: "Select a booking form",
5884
- empty: "No booking forms yet",
5885
- emptySelection: "Select a booking form to enable appointment booking."
5886
- }
5887
- },
5888
- appointmentService: {
5889
- labels: {
5890
- loading: "Loading appointment services\u2026",
5891
- select: "Select a service",
5892
- empty: "No services available",
5893
- emptySelection: "Select a service to enable booking."
6318
+ function getValueAtPath(source, path) {
6319
+ if (!path) return source;
6320
+ let current = source;
6321
+ for (const segment of path.split(".")) {
6322
+ if (!current || typeof current !== "object") {
6323
+ return void 0;
5894
6324
  }
5895
- },
5896
- appointmentResource: {
5897
- labels: {
5898
- loading: "Loading resources\u2026",
5899
- select: "Select a resource",
5900
- empty: "No resources available",
5901
- emptySelection: "Select a resource (optional)."
5902
- }
5903
- },
5904
- page: {
5905
- labels: {
5906
- loading: "Loading pages\u2026",
5907
- select: "Select a page",
5908
- empty: "No pages available",
5909
- emptySelection: "Select a page."
5910
- }
5911
- },
5912
- newsletterList: {
5913
- labels: {
5914
- loading: "Loading lists\u2026",
5915
- select: "Select a list",
5916
- empty: "No lists yet",
5917
- emptySelection: "Select a newsletter list to subscribe users to."
5918
- }
5919
- }
5920
- };
5921
- function createPreviewReferenceAdapters(previewClient, fallbackSiteId) {
5922
- const brandedFallbackSiteId = void 0;
5923
- const adapters = {};
5924
- for (const kind of BUILT_IN_REFERENCE_KINDS) {
5925
- adapters[kind] = {
5926
- loadItems: createCachedReferenceAdapterLoader(
5927
- kind,
5928
- referenceLoaders[kind],
5929
- previewClient,
5930
- brandedFallbackSiteId
5931
- ),
5932
- labels: previewReferenceChrome[kind].labels
5933
- };
6325
+ current = current[segment];
5934
6326
  }
5935
- return createDynamicReferenceAdapters(
5936
- adapters,
5937
- (contentTypeKey) => createCachedDynamicContentReferenceAdapter(contentTypeKey, previewClient, brandedFallbackSiteId)
5938
- );
5939
- }
5940
- function PreviewFormProviderInner({ theme, children }) {
5941
- const { onPickImage, picker } = useRichTextImagePicker();
5942
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5943
- /* @__PURE__ */ jsx(RichTextWidgetProvider, { theme, onPickImage, children }),
5944
- picker
5945
- ] });
5946
- }
5947
- function PreviewFormProvider({
5948
- siteId: rawSiteId,
5949
- theme = null,
5950
- previewClient,
5951
- bearerToken,
5952
- supabaseUrl,
5953
- mediaAssets,
5954
- children
5955
- }) {
5956
- const siteId = useMemo(() => expectParsedBrandedId(parseSiteId(rawSiteId)), [rawSiteId]);
5957
- const linkAdapters = useMemo(() => createPreviewLinkAdapters(previewClient), [previewClient]);
5958
- const entryPickerAdapter = useMemo(() => createPreviewEntryPickerAdapter(previewClient), [previewClient]);
5959
- const referenceAdapters = useMemo(
5960
- () => createPreviewReferenceAdapters(previewClient),
5961
- [previewClient]
5962
- );
5963
- const paletteTokens = useMemo(() => {
5964
- if (!theme) return void 0;
5965
- try {
5966
- const runtime = buildThemeRuntime(theme);
5967
- return runtime.paletteTokens;
5968
- } catch {
5969
- return void 0;
5970
- }
5971
- }, [theme]);
5972
- return /* @__PURE__ */ jsx(PaletteProvider, { tokens: paletteTokens, children: /* @__PURE__ */ jsx(LinkWidgetProvider, { siteId, adapters: linkAdapters, children: /* @__PURE__ */ jsx(ReferenceWidgetProvider, { siteId, adapters: referenceAdapters, children: /* @__PURE__ */ jsx(EntryPickerWidgetProvider, { siteId, adapter: entryPickerAdapter, children: /* @__PURE__ */ jsx(
5973
- MediaEditorProvider,
5974
- {
5975
- siteId,
5976
- apiClient: previewClient,
5977
- bearerToken,
5978
- supabaseUrl,
5979
- mediaAssets,
5980
- children: /* @__PURE__ */ jsx(ButtonWidgetProvider, { buttonSystem: theme?.buttons ?? null, children: /* @__PURE__ */ jsx(PreviewFormProviderInner, { theme, children }) })
5981
- }
5982
- ) }) }) }) });
5983
- }
5984
- var previewWidgetOverrides = {
5985
- link: LinkField,
5986
- file: FileField,
5987
- media: MediaField,
5988
- richText: RichTextField,
5989
- reference: ReferenceField,
5990
- backgroundColor: BackgroundColorField,
5991
- backgroundGradient: BackgroundGradientField,
5992
- entryPicker: EntryPickerField,
5993
- iconPicker: IconPickerField,
5994
- buttonVariant: ButtonVariantField
5995
- };
5996
- function PreviewZoomControls({ zoom, onZoomChange, disabled }) {
5997
- return /* @__PURE__ */ jsxs(
5998
- Select,
5999
- {
6000
- value: String(zoom),
6001
- onValueChange: (value) => onZoomChange(value === "fit" ? "fit" : Number(value)),
6002
- disabled,
6003
- children: [
6004
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-20", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
6005
- /* @__PURE__ */ jsxs(SelectContent, { children: [
6006
- /* @__PURE__ */ jsx(SelectItem, { value: "33", children: "33%" }),
6007
- /* @__PURE__ */ jsx(SelectItem, { value: "50", children: "50%" }),
6008
- /* @__PURE__ */ jsx(SelectItem, { value: "75", children: "75%" }),
6009
- /* @__PURE__ */ jsx(SelectItem, { value: "100", children: "100%" }),
6010
- /* @__PURE__ */ jsx(SelectItem, { value: "fit", children: "Fit" })
6011
- ] })
6012
- ]
6013
- }
6014
- );
6015
- }
6016
- var VIEWPORT_PRESETS = [
6017
- { label: "Mobile", width: 375, icon: Smartphone },
6018
- { label: "Tablet", width: 768, icon: Tablet },
6019
- { label: "Desktop", width: 1440, icon: Monitor },
6020
- { label: "Full Width", width: "full", icon: Maximize2 }
6021
- ];
6022
- function PreviewViewportControls({
6023
- mode,
6024
- width,
6025
- onModeChange,
6026
- onWidthChange,
6027
- className,
6028
- zoom,
6029
- onZoomChange
6030
- }) {
6031
- const activePreset = VIEWPORT_PRESETS.find((p) => p.width === width);
6032
- const ActiveIcon = activePreset?.icon ?? Monitor;
6033
- const activeLabel = mode === "squeeze" ? "Auto fit" : activePreset?.label ?? `${width}px`;
6034
- const showZoomControls = mode === "fixed" && zoom !== void 0 && onZoomChange !== void 0;
6035
- return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), children: [
6036
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
6037
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "gap-2", children: [
6038
- /* @__PURE__ */ jsx(ActiveIcon, { className: "h-4 w-4" }),
6039
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: activeLabel })
6040
- ] }) }),
6041
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
6042
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Preview size" }),
6043
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
6044
- /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => onModeChange("squeeze"), className: cn(mode === "squeeze" && "bg-accent"), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
6045
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Auto fit" }),
6046
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Matches the space beside the editor" })
6047
- ] }) }),
6048
- /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => onModeChange("fixed"), className: cn(mode === "fixed" && "bg-accent"), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
6049
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Choose size" }),
6050
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Preview mobile, tablet, or desktop widths" })
6051
- ] }) })
6052
- ] })
6053
- ] }),
6054
- mode === "fixed" && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
6055
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: activePreset?.label ?? `${width}px` }) }),
6056
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
6057
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Screen size" }),
6058
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
6059
- VIEWPORT_PRESETS.map((preset) => /* @__PURE__ */ jsxs(
6060
- DropdownMenuItem,
6061
- {
6062
- onClick: () => onWidthChange(preset.width),
6063
- className: cn(width === preset.width && "bg-accent"),
6064
- children: [
6065
- /* @__PURE__ */ jsx(preset.icon, { className: "mr-2 h-4 w-4" }),
6066
- preset.label,
6067
- typeof preset.width === "number" && /* @__PURE__ */ jsxs("span", { className: "ml-auto text-xs text-muted-foreground", children: [
6068
- preset.width,
6069
- "px"
6070
- ] })
6071
- ]
6072
- },
6073
- preset.label
6074
- ))
6075
- ] })
6076
- ] }),
6077
- showZoomControls ? /* @__PURE__ */ jsx(PreviewZoomControls, { zoom, onZoomChange }) : null
6078
- ] });
6079
- }
6080
- function SubrouteNotice({ model }) {
6081
- return /* @__PURE__ */ jsxs("div", { className: "border-b bg-amber-50 px-4 py-3 text-amber-950", children: [
6082
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: model.title }),
6083
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm", children: model.body }),
6084
- model.cta ? /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsx(
6085
- "a",
6086
- {
6087
- className: "inline-flex items-center rounded-md bg-white px-3 py-1.5 text-sm font-medium text-amber-950 shadow-sm ring-1 ring-amber-900/10 hover:bg-amber-100",
6088
- href: model.cta.href,
6089
- target: "_blank",
6090
- rel: "noopener noreferrer",
6091
- children: model.cta.label
6092
- }
6093
- ) }) : null,
6094
- model.fallbackMessage ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-amber-900/90", children: model.fallbackMessage }) : null
6095
- ] });
6327
+ return current;
6096
6328
  }
6097
6329
 
6098
- // ../preview-next/src/client/preview/subrouteNotice.ts
6099
- function buildNoticeFromMeta(meta, payload, siteId) {
6100
- const name = meta.formatInstanceName(payload);
6101
- const title = name ? `You're previewing the ${name} ${meta.instanceNoun} of this ${meta.containerNoun}` : `You're previewing one ${meta.instanceNoun} of this ${meta.containerNoun}`;
6102
- const body = `Edits in this sidebar apply to the whole ${meta.containerNoun}, so they'll show up on every ${meta.instanceNoun}. Need to change just this ${meta.instanceNoun}? You can do that from your ${meta.listLabel}.`;
6103
- const ctaLabel = `Edit just this ${meta.instanceNoun}`;
6104
- let href = null;
6105
- try {
6106
- href = getDashboardUrl(`sites/${siteId}/${meta.subEditorPath(payload)}`);
6107
- } catch {
6108
- href = null;
6109
- }
6110
- return {
6111
- title,
6112
- body,
6113
- cta: href ? { label: ctaLabel, href } : null,
6114
- fallbackMessage: href ? null : "Dashboard links are unavailable. Set `NEXT_PUBLIC_DASHBOARD_URL` so preview can link back to the dashboard."
6115
- };
6116
- }
6117
- function resolveSubrouteNotice(ctx, args) {
6118
- if (!ctx) return null;
6119
- return foldSubroute(ctx, {
6120
- "event-occurrence": (occurrence) => buildNoticeFromMeta(SUBROUTE_KINDS["event-occurrence"], occurrence, args.siteId),
6121
- "course-run": (run) => buildNoticeFromMeta(SUBROUTE_KINDS["course-run"], run, args.siteId)
6122
- });
6123
- }
6124
- function EntryEditorPanel({
6125
- siteId,
6126
- entryData,
6127
- contentTypeSchema,
6128
- referenceContentTypeKeys,
6129
- draftContent,
6130
- currentTemplate,
6330
+ // ../preview-next/src/client/blocks/useBlockCustomizeControls.ts
6331
+ var MAX_CUSTOMIZE_POPOVER_ROWS = 6;
6332
+ var MAX_QUICK_MANIFEST_FIELDS = 3;
6333
+ function useBlockCustomizeControls({
6334
+ blockId,
6335
+ blockKind,
6336
+ content,
6337
+ uiStore,
6131
6338
  theme,
6132
- apiClient,
6133
- bearerToken,
6134
- supabaseUrl,
6135
- entrySubrouteContext,
6136
- metadataPanel,
6137
- mediaAssets,
6138
- widgets,
6139
- viewportMode,
6140
- viewportWidth,
6141
- zoom,
6142
- onViewportModeChange,
6143
- onViewportWidthChange,
6144
- onZoomChange
6339
+ sdkConfig,
6340
+ maxQuickFields = MAX_QUICK_MANIFEST_FIELDS,
6341
+ maxPopoverRows,
6342
+ builtInPopoverRows = 0
6145
6343
  }) {
6146
- const [contentOpen, setContentOpen] = useState(true);
6147
- const [templateOpen, setTemplateOpen] = useState(false);
6148
- const [detailsOpen, setDetailsOpen] = useState(false);
6149
- const contentFormFields = useMemo(
6150
- // content-model keeps a source-local structural field type so it can typecheck natively
6151
- // without importing @riverbankcms/blocks source. Preview is the integration boundary where
6152
- // those normalized fields are consumed as a block-form manifest.
6153
- () => contentTypeSchema ? mapContentSchemaToFormFields(contentTypeSchema, {
6154
- referenceContentTypeKeys
6155
- }) : [],
6156
- [contentTypeSchema, referenceContentTypeKeys]
6344
+ const {
6345
+ blockFieldOptions,
6346
+ blockFieldExtensions,
6347
+ sdkConfig: contextSdkConfig
6348
+ } = useSdkConfig();
6349
+ const effectiveSdkConfig = sdkConfig ?? contextSdkConfig;
6350
+ const manifest = useMemo(
6351
+ () => resolveBlockManifest(blockKind, effectiveSdkConfig ?? null),
6352
+ [blockKind, effectiveSdkConfig]
6157
6353
  );
6158
- const contentFormSpec = useMemo(
6159
- () => ({
6160
- name: "content-fields",
6161
- version: "1.0",
6162
- title: "Content",
6163
- component: "content-form",
6164
- description: "",
6165
- fields: contentFormFields,
6166
- slots: []
6354
+ const preparedManifest = useMemo(() => {
6355
+ if (!manifest) return null;
6356
+ return prepareBlockEditorManifest({
6357
+ manifest,
6358
+ blockKind,
6359
+ styleGroups: theme?.styleGroups,
6360
+ blockFieldOptions,
6361
+ blockFieldExtensions,
6362
+ visibilityPolicy: hideThemeV2RawStyleFields
6363
+ });
6364
+ }, [
6365
+ blockFieldExtensions,
6366
+ blockFieldOptions,
6367
+ blockKind,
6368
+ manifest,
6369
+ theme?.styleGroups
6370
+ ]);
6371
+ const fields = useMemo(() => {
6372
+ if (!preparedManifest) return [];
6373
+ return createManifestFormAdapter(preparedManifest.renderManifest).fields;
6374
+ }, [preparedManifest]);
6375
+ const preliminaryPlan = useMemo(
6376
+ () => createPreviewCustomizeControlPlan({
6377
+ blockKind,
6378
+ fields,
6379
+ content,
6380
+ maxQuickFields
6167
6381
  }),
6168
- [contentFormFields]
6382
+ [blockKind, content, fields, maxQuickFields]
6169
6383
  );
6170
- const contentInitialValues = useMemo(() => draftContent ?? {}, [draftContent]);
6171
- const subrouteNotice = useMemo(
6172
- () => resolveSubrouteNotice(entrySubrouteContext ?? null, { siteId }),
6173
- [entrySubrouteContext, siteId]
6174
- );
6175
- return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", children: [
6176
- /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-2", children: /* @__PURE__ */ jsx(
6177
- PreviewViewportControls,
6178
- {
6179
- mode: viewportMode,
6180
- width: viewportWidth,
6181
- onModeChange: onViewportModeChange,
6182
- onWidthChange: onViewportWidthChange,
6183
- zoom,
6184
- onZoomChange
6185
- }
6186
- ) }),
6187
- subrouteNotice ? /* @__PURE__ */ jsx(SubrouteNotice, { model: subrouteNotice }) : null,
6188
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-0 overflow-auto", children: [
6189
- contentTypeSchema && entryData.type ? /* @__PURE__ */ jsx(Collapsible, { open: contentOpen, onOpenChange: setContentOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6190
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6191
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${contentOpen ? "rotate-90" : ""}` }),
6192
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Content" })
6193
- ] }) }),
6194
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
6195
- PreviewFormProvider,
6196
- {
6197
- siteId,
6198
- theme: theme ?? null,
6199
- previewClient: apiClient,
6200
- bearerToken,
6201
- supabaseUrl,
6202
- mediaAssets,
6203
- children: /* @__PURE__ */ jsx(
6204
- ContentFieldsForm,
6205
- {
6206
- siteId,
6207
- form: contentFormSpec,
6208
- initialValues: contentInitialValues,
6209
- widgets
6210
- }
6211
- )
6212
- }
6213
- ) }) })
6214
- ] }) }) : /* @__PURE__ */ jsx("div", { className: "border-b px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed px-4 py-6 text-sm text-muted-foreground", children: "Content schema not available for this entry type." }) }),
6215
- currentTemplate && entryData.type ? /* @__PURE__ */ jsx(Collapsible, { open: templateOpen, onOpenChange: setTemplateOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6216
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6217
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${templateOpen ? "rotate-90" : ""}` }),
6218
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Template" })
6219
- ] }) }),
6220
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-4", children: [
6221
- /* @__PURE__ */ jsx("div", { className: "rounded-md border px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6222
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: currentTemplate.name }),
6223
- currentTemplate.description ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: currentTemplate.description }) : null
6224
- ] }) }),
6225
- /* @__PURE__ */ jsx(
6226
- "a",
6227
- {
6228
- href: (() => {
6229
- try {
6230
- return getDashboardUrl(`sites/${siteId}/content/${entryData.type}/template`);
6231
- } catch {
6232
- return "#";
6233
- }
6234
- })(),
6235
- target: "_blank",
6236
- rel: "noopener noreferrer",
6237
- className: "inline-flex items-center text-sm text-primary hover:underline",
6238
- children: "Edit template in dashboard \u2192"
6239
- }
6240
- )
6241
- ] }) })
6242
- ] }) }) : null,
6243
- metadataPanel ? /* @__PURE__ */ jsx(Collapsible, { open: detailsOpen, onOpenChange: setDetailsOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6244
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6245
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${detailsOpen ? "rotate-90" : ""}` }),
6246
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Details" })
6247
- ] }) }),
6248
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: metadataPanel }) })
6249
- ] }) }) : null
6250
- ] })
6251
- ] });
6252
- }
6253
- function PageBlockEditorPanel({
6254
- siteId,
6255
- pageId,
6256
- theme,
6257
- apiClient,
6258
- bearerToken,
6259
- supabaseUrl,
6260
- blockItems,
6261
- uiStoreForBlockEditor,
6262
- blockEditorRef,
6263
- onBlocksChange,
6264
- metadataPanel,
6265
- designPanel,
6266
- sdkConfig,
6267
- mediaAssets,
6268
- viewportMode,
6269
- viewportWidth,
6270
- zoom,
6271
- onViewportModeChange,
6272
- onViewportWidthChange,
6273
- onZoomChange
6274
- }) {
6275
- const [designOpen, setDesignOpen] = useState(false);
6276
- const [contentOpen, setContentOpen] = useState(true);
6277
- const [detailsOpen, setDetailsOpen] = useState(false);
6278
- return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", children: [
6279
- /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-2", children: /* @__PURE__ */ jsx(
6280
- PreviewViewportControls,
6281
- {
6282
- mode: viewportMode,
6283
- width: viewportWidth,
6284
- onModeChange: onViewportModeChange,
6285
- onWidthChange: onViewportWidthChange,
6286
- zoom,
6287
- onZoomChange
6288
- }
6289
- ) }),
6290
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-0 overflow-auto", children: [
6291
- designPanel ? /* @__PURE__ */ jsx(Collapsible, { open: designOpen, onOpenChange: setDesignOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6292
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6293
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${designOpen ? "rotate-90" : ""}` }),
6294
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Design" })
6295
- ] }) }),
6296
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: designPanel }) })
6297
- ] }) }) : null,
6298
- /* @__PURE__ */ jsx(Collapsible, { open: contentOpen, onOpenChange: setContentOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6299
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6300
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${contentOpen ? "rotate-90" : ""}` }),
6301
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Page Blocks" })
6302
- ] }) }),
6303
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
6304
- PreviewFormProvider,
6305
- {
6306
- siteId,
6307
- theme: theme ?? null,
6308
- previewClient: apiClient,
6309
- bearerToken,
6310
- supabaseUrl,
6311
- mediaAssets,
6312
- children: /* @__PURE__ */ jsx(
6313
- BlockEditor,
6314
- {
6315
- ref: blockEditorRef,
6316
- siteId,
6317
- pageId,
6318
- blocks: blockItems,
6319
- uiStore: uiStoreForBlockEditor,
6320
- apiClient,
6321
- onBlocksChange,
6322
- widgetOverrides: previewWidgetOverrides,
6323
- sdkConfig: sdkConfig ?? null,
6324
- reorderControlMode: "drag-handle-only",
6325
- actionControlMode: "menu"
6326
- },
6327
- pageId
6328
- )
6329
- }
6330
- ) }) })
6331
- ] }) }),
6332
- metadataPanel ? /* @__PURE__ */ jsx(Collapsible, { open: detailsOpen, onOpenChange: setDetailsOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
6333
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40", children: [
6334
- /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${detailsOpen ? "rotate-90" : ""}` }),
6335
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Page Details" })
6336
- ] }) }),
6337
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: metadataPanel }) })
6338
- ] }) }) : null
6339
- ] })
6340
- ] });
6341
- }
6342
- function PreviewEditorGuidanceCard({
6343
- guidance,
6344
- completeSetupDisabled,
6345
- onCompleteSetup
6346
- }) {
6347
- return /* @__PURE__ */ jsxs(
6348
- "div",
6349
- {
6350
- className: guidance.cardClassName,
6351
- "data-preview-guidance-tone": guidance.tone,
6352
- children: [
6353
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-tight text-slate-900", children: guidance.title }),
6354
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-5 text-slate-700", children: guidance.description }),
6355
- /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
6356
- Button,
6357
- {
6358
- type: "button",
6359
- size: "sm",
6360
- variant: "outline",
6361
- onClick: onCompleteSetup,
6362
- disabled: completeSetupDisabled,
6363
- children: guidance.ctaLabel
6364
- }
6365
- ) })
6366
- ]
6384
+ const budgetedMaxQuickFields = resolveQuickFieldBudget({
6385
+ requestedMaxQuickFields: maxQuickFields,
6386
+ maxPopoverRows,
6387
+ builtInPopoverRows,
6388
+ hasOverflowControls: preliminaryPlan.panelControls.length > 0
6389
+ });
6390
+ const controlPlan = useMemo(() => {
6391
+ if (budgetedMaxQuickFields === maxQuickFields) {
6392
+ return preliminaryPlan;
6367
6393
  }
6394
+ return createPreviewCustomizeControlPlan({
6395
+ blockKind,
6396
+ fields,
6397
+ content,
6398
+ maxQuickFields: budgetedMaxQuickFields
6399
+ });
6400
+ }, [
6401
+ blockKind,
6402
+ budgetedMaxQuickFields,
6403
+ content,
6404
+ fields,
6405
+ maxQuickFields,
6406
+ preliminaryPlan
6407
+ ]);
6408
+ const quickFields = useMemo(
6409
+ () => controlPlan.quickControls.map((control) => stripTopLevelRoutingUi(control.field)),
6410
+ [controlPlan.quickControls]
6368
6411
  );
6369
- }
6370
-
6371
- // ../ui/src/palette/hex.ts
6372
- function normalizeHex(input, fallback = "#000000") {
6373
- if (!input) return fallback;
6374
- const trimmed = input.trim().replace(/^#/, "").toLowerCase();
6375
- const expanded = trimmed.length === 3 ? trimmed.split("").map((char) => char + char).join("") : trimmed;
6376
- if (!/^[0-9a-f]{6}$/i.test(expanded)) {
6377
- return fallback;
6378
- }
6379
- return `#${expanded}`;
6380
- }
6381
- function hexToRgb(hex) {
6382
- const normalized = normalizeHex(hex).replace("#", "");
6383
- const match = normalized.match(/.{2}/g);
6384
- if (!match || match.length < 3) return null;
6385
- const [r, g, b] = match.map((value) => parseInt(value, 16));
6386
- return [r, g, b];
6387
- }
6388
- function luminanceChannel(raw) {
6389
- const channel = raw / 255;
6390
- return channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
6391
- }
6392
- function contrastRatio(hexA, hexB) {
6393
- const rgbA = hexToRgb(hexA);
6394
- const rgbB = hexToRgb(hexB);
6395
- if (!rgbA || !rgbB) return 1;
6396
- const lumA = 0.2126 * luminanceChannel(rgbA[0]) + 0.7152 * luminanceChannel(rgbA[1]) + 0.0722 * luminanceChannel(rgbA[2]);
6397
- const lumB = 0.2126 * luminanceChannel(rgbB[0]) + 0.7152 * luminanceChannel(rgbB[1]) + 0.0722 * luminanceChannel(rgbB[2]);
6398
- const brighter = Math.max(lumA, lumB);
6399
- const darker = Math.min(lumA, lumB);
6400
- return Number(((brighter + 0.05) / (darker + 0.05)).toFixed(2));
6401
- }
6402
- var DEFAULT_BRAND_ROLES = [
6403
- { key: "primary", label: "Primary" },
6404
- { key: "secondary", label: "Secondary" },
6405
- { key: "accent", label: "Accent" }
6406
- ];
6407
- var DEFAULT_FEEDBACK_ROLES = [
6408
- { key: "success", label: "Success" },
6409
- { key: "warning", label: "Warning" },
6410
- { key: "destructive", label: "Destructive" }
6411
- ];
6412
- var DEFAULT_SURFACE_ROLES = [
6413
- { key: "background", label: "Background" },
6414
- { key: "surface", label: "Surface" },
6415
- { key: "text", label: "Text" },
6416
- { key: "border", label: "Border" }
6417
- ];
6418
- function PaletteTokenEditor({
6419
- palette,
6420
- onChange,
6421
- hasInvalidValues = false,
6422
- brandRoles = DEFAULT_BRAND_ROLES,
6423
- feedbackRoles = DEFAULT_FEEDBACK_ROLES,
6424
- surfaceRoles = DEFAULT_SURFACE_ROLES,
6425
- showContrastChecks = true,
6426
- className
6427
- }) {
6428
- const paletteValues = useMemo(() => {
6429
- const map = /* @__PURE__ */ new Map();
6430
- for (const entry of palette) {
6431
- if (entry?.name) map.set(entry.name, normalizeHex(entry.hex));
6432
- }
6433
- return map;
6434
- }, [palette]);
6435
- const getColor = (role) => paletteValues.get(role) ?? "#000000";
6436
- const handleRoleChange = (role, hex) => {
6437
- if (!isPlausibleHexInput(hex)) return;
6438
- onChange(role, normalizeHex(hex));
6439
- };
6440
- const backgroundHex = getColor("background");
6441
- const textHex = getColor("text");
6442
- const primaryHex = getColor("primary");
6443
- const contrast = contrastRatio(backgroundHex, textHex);
6444
- const actionContrast = contrastRatio(primaryHex, backgroundHex);
6445
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
6446
- hasInvalidValues ? /* @__PURE__ */ jsx(Callout, { tone: "danger", role: "alert", className: "p-3", children: /* @__PURE__ */ jsx(CalloutDescription, { className: "mt-0", children: "Some palette values are invalid or incomplete." }) }) : null,
6447
- /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Brand & accents", roles: brandRoles, getColor, onChange: handleRoleChange }),
6448
- /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Feedback", roles: feedbackRoles, getColor, onChange: handleRoleChange }),
6449
- /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Surfaces & text", roles: surfaceRoles, getColor, onChange: handleRoleChange }),
6450
- showContrastChecks ? /* @__PURE__ */ jsxs(Fragment, { children: [
6451
- /* @__PURE__ */ jsx(Separator, {}),
6452
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
6453
- /* @__PURE__ */ jsx(
6454
- ContrastCard,
6455
- {
6456
- title: "Background \u2194 Text contrast",
6457
- value: contrast,
6458
- threshold: 4.5,
6459
- primaryLabel: "Background",
6460
- primaryHex: backgroundHex,
6461
- secondaryLabel: "Text",
6462
- secondaryHex: textHex
6463
- }
6464
- ),
6465
- /* @__PURE__ */ jsx(
6466
- ContrastCard,
6467
- {
6468
- title: "Primary action contrast",
6469
- value: actionContrast,
6470
- threshold: 3,
6471
- primaryLabel: "Primary",
6472
- primaryHex,
6473
- secondaryLabel: "Background",
6474
- secondaryHex: backgroundHex
6475
- }
6476
- )
6477
- ] })
6478
- ] }) : null
6479
- ] });
6480
- }
6481
- function isPlausibleHexInput(value) {
6482
- const trimmed = value.trim();
6483
- return /^#?[0-9a-fA-F]{3}$/.test(trimmed) || /^#?[0-9a-fA-F]{6}$/.test(trimmed);
6484
- }
6485
- function ColorRoleGroup({ title, roles, getColor, onChange }) {
6486
- if (roles.length === 0) return null;
6487
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
6488
- /* @__PURE__ */ jsxs("div", { children: [
6489
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-foreground", children: title }),
6490
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Adjust hex values or drag the color picker." })
6491
- ] }),
6492
- /* @__PURE__ */ jsx("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3", children: roles.map((role) => /* @__PURE__ */ jsx(ColorRoleField, { role, value: getColor(role.key), onChange }, role.key)) })
6493
- ] });
6494
- }
6495
- function ColorRoleField({ role, value, onChange }) {
6496
- const inputId = useId();
6497
- const colorPickerLabel = `${role.label} colour picker`;
6498
- return /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted p-3 space-y-2", children: [
6499
- /* @__PURE__ */ jsx(
6500
- "label",
6501
- {
6502
- htmlFor: inputId,
6503
- className: "text-xs uppercase text-muted-foreground font-medium leading-5 block",
6504
- children: role.label
6505
- }
6506
- ),
6507
- /* @__PURE__ */ jsx(
6508
- ColorPicker,
6509
- {
6510
- value,
6511
- onChange: ((next) => {
6512
- if (typeof next === "string") {
6513
- onChange(role.key, next);
6514
- }
6515
- }),
6516
- className: "w-full",
6517
- "aria-label": colorPickerLabel
6518
- }
6519
- ),
6520
- /* @__PURE__ */ jsx(
6521
- Input,
6522
- {
6523
- id: inputId,
6524
- value,
6525
- onChange: (event) => onChange(role.key, event.target.value),
6526
- className: "font-mono text-xs uppercase",
6527
- placeholder: "#000000"
6528
- }
6529
- )
6530
- ] });
6531
- }
6532
- function ContrastCard({ title, value, threshold, primaryLabel, primaryHex, secondaryLabel, secondaryHex }) {
6533
- const passes = value >= threshold;
6534
- return /* @__PURE__ */ jsxs(
6535
- Surface,
6536
- {
6537
- variant: "background",
6538
- radius: "lg",
6539
- padding: "md",
6540
- className: cn(
6541
- "space-y-2 border text-sm",
6542
- passes ? "border-success/40 bg-success/10 text-success" : "border-warning/40 bg-warning/10 text-warning-foreground"
6543
- ),
6544
- children: [
6545
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
6546
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: title }),
6547
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold", children: [
6548
- value,
6549
- ":1"
6550
- ] })
6551
- ] }),
6552
- /* @__PURE__ */ jsx("p", { className: "text-xs", children: passes ? "Contrast meets WCAG AA guidance." : `Increase contrast to reach ${threshold}:1 or higher.` }),
6553
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-2 text-xs", children: [
6554
- /* @__PURE__ */ jsx(ContrastSwatch, { hex: primaryHex, label: primaryLabel }),
6555
- /* @__PURE__ */ jsx(ContrastSwatch, { hex: secondaryHex, label: secondaryLabel })
6556
- ] })
6557
- ]
6558
- }
6412
+ const panelFields = useMemo(
6413
+ () => controlPlan.panelControls.map((control) => stripTopLevelRoutingUi(control.field)),
6414
+ [controlPlan.panelControls]
6559
6415
  );
6560
- }
6561
- function ContrastSwatch({ hex, label }) {
6562
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
6563
- /* @__PURE__ */ jsx("span", { className: "h-6 w-6 rounded-full border", style: { backgroundColor: hex } }),
6564
- /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs uppercase tracking-wide text-muted-foreground", children: [
6565
- label,
6566
- ": ",
6567
- hex
6568
- ] })
6569
- ] });
6570
- }
6571
-
6572
- // ../preview-next/src/client/preview/styleConfiguratorApplyPayload.ts
6573
- function buildApplySiteStyleSelectionBody(input) {
6416
+ const backend = useMemo(() => {
6417
+ if (!preparedManifest) return null;
6418
+ return createStoreBackend({
6419
+ scope: "blocks",
6420
+ store: uiStore,
6421
+ blockId,
6422
+ validateFn: (values) => validateBlockManifestIssues(preparedManifest.validationManifest, values, "strict")
6423
+ });
6424
+ }, [blockId, preparedManifest, uiStore]);
6574
6425
  return {
6575
- siteStyleId: input.previewed.siteStyleId,
6576
- buttonPersonalityId: input.previewed.buttonPersonalityId,
6577
- paletteVariantId: input.previewed.paletteVariantId,
6578
- paletteOverrides: input.previewed.paletteOverrides,
6579
- typographyPresetId: input.previewed.typographyPresetId,
6580
- typographyBodyFontOverride: input.previewed.typographyBodyFontOverride,
6581
- typographyHeadingFontOverride: input.previewed.typographyHeadingFontOverride,
6582
- headerLookId: input.previewed.headerLookId,
6583
- headerLayoutId: input.previewed.headerLayoutId,
6584
- headerStyleId: input.previewed.headerStyleId,
6585
- footerLookId: input.previewed.footerLookId,
6586
- baseSelectionVersion: input.base.selectionVersion,
6587
- selectionId: input.selectionId ?? null
6426
+ manifest,
6427
+ controlPlan,
6428
+ quickFields,
6429
+ panelFields,
6430
+ backend,
6431
+ hasControls: quickFields.length > 0 || panelFields.length > 0,
6432
+ hasOverflowControls: panelFields.length > 0
6588
6433
  };
6589
6434
  }
6590
- var MAX_FONT_RESULTS_WHEN_SEARCHING = 12;
6591
- var MAX_FONT_RESULTS_WHEN_BROWSING = 8;
6592
- function StyleConfigurator({
6593
- siteId,
6594
- apiClient,
6595
- theme,
6596
- selectionId,
6597
- selectionVersion = null,
6598
- selectionUpdatedAt,
6599
- initialSelections,
6600
- headerSelectionContext,
6601
- footerSelectionContext,
6602
- expandedRowRequest = null,
6603
- siteStyleAccess
6435
+ function resolveQuickFieldBudget({
6436
+ requestedMaxQuickFields,
6437
+ maxPopoverRows,
6438
+ builtInPopoverRows,
6439
+ hasOverflowControls
6604
6440
  }) {
6605
- const router = useRouter();
6606
- const previewDesign = usePreviewDesignCoordinator();
6607
- const {
6608
- applyFailed,
6609
- applyStarted,
6610
- applySucceeded,
6611
- clearPreview,
6612
- previewTheme
6613
- } = previewDesign;
6614
- const [isRefreshPending, startRefreshTransition] = useTransition();
6615
- const baseSnapshot = useMemo(
6616
- () => resolveStyleConfiguratorInitialSnapshot({
6617
- theme,
6618
- initialSelections: {
6619
- ...initialSelections,
6620
- selectionVersion: initialSelections.selectionVersion ?? selectionVersion ?? 0,
6621
- selectionUpdatedAt: initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
6622
- },
6623
- headerSelectionContext,
6624
- footerSelectionContext
6625
- }),
6626
- [footerSelectionContext, headerSelectionContext, theme, initialSelections, selectionUpdatedAt, selectionVersion]
6441
+ const cappedRequested = Math.max(
6442
+ 0,
6443
+ Math.min(MAX_QUICK_MANIFEST_FIELDS, requestedMaxQuickFields)
6627
6444
  );
6628
- const [state, setState] = useState(
6629
- () => baseSnapshot ? createStyleConfiguratorState(baseSnapshot) : null
6630
- );
6631
- const hasLoadedState = state !== null;
6632
- const stateRef = useRef(state);
6633
- const clearPreviewRef = useRef(clearPreview);
6634
- const runnerRef = useRef(createOptimisticActionRunner());
6635
- const appliedExpandedRowRequestIdRef = useRef(null);
6636
- useEffect(() => {
6637
- stateRef.current = state;
6638
- }, [state]);
6639
- useEffect(() => {
6640
- clearPreviewRef.current = clearPreview;
6641
- }, [clearPreview]);
6642
- useEffect(() => {
6643
- if (!baseSnapshot) return;
6644
- setState((current) => current ? syncStyleConfiguratorState(current, baseSnapshot) : createStyleConfiguratorState(baseSnapshot));
6645
- }, [baseSnapshot]);
6646
- useEffect(() => {
6647
- if (!state || !baseSnapshot) {
6648
- clearPreview("style-configurator");
6649
- return;
6650
- }
6651
- if (!isPreviewingChange(state)) {
6652
- clearPreview("style-configurator");
6653
- return;
6654
- }
6655
- const siteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
6656
- if (!siteStyle) {
6657
- clearPreview("style-configurator");
6658
- return;
6659
- }
6660
- const result = planSiteStylePreviewChange({
6661
- siteStyle,
6662
- selection: state.previewed,
6663
- baseTheme: theme,
6664
- headerSelectionContext,
6665
- footerSelectionContext
6666
- });
6667
- previewTheme("style-configurator", result.themeOverlay, "site-style");
6668
- }, [state, baseSnapshot, clearPreview, previewTheme, headerSelectionContext, footerSelectionContext, theme]);
6669
- useEffect(() => {
6670
- const runner = runnerRef.current;
6671
- return () => {
6672
- runner.invalidate();
6673
- clearPreviewRef.current("style-configurator");
6674
- };
6675
- }, []);
6676
- useEffect(() => {
6677
- if (!expandedRowRequest) return;
6678
- if (appliedExpandedRowRequestIdRef.current === expandedRowRequest.id) return;
6679
- setState((current) => {
6680
- if (!current) return current;
6681
- appliedExpandedRowRequestIdRef.current = expandedRowRequest.id;
6682
- return openRow(current, expandedRowRequest.row === "header" ? "header-layout" : expandedRowRequest.row);
6683
- });
6684
- }, [expandedRowRequest, hasLoadedState]);
6685
- const handlePreviewSiteStyle = useCallback((siteStyleId) => {
6686
- setState((current) => {
6687
- if (!current) return current;
6688
- const next = previewSiteStyle(current, siteStyleId);
6689
- const siteStyle = getCuratedSiteStyle(siteStyleId);
6690
- if (!siteStyle) return next;
6691
- const result = planSiteStylePreviewChange({
6692
- siteStyle,
6693
- selection: next.previewed,
6694
- baseTheme: theme,
6695
- headerSelectionContext,
6696
- footerSelectionContext
6697
- });
6698
- return applyResolvedSelections(next, {
6699
- resolved: result.resolvedSelections,
6700
- warnings: result.warnings
6701
- });
6702
- });
6703
- }, [footerSelectionContext, headerSelectionContext, theme]);
6704
- const handlePreviewPersonality = useCallback((id) => {
6705
- setState((current) => current ? previewButtonPersonality(current, id) : current);
6706
- }, []);
6707
- const handlePreviewHeaderLayout = useCallback((id) => {
6708
- if (!id) return;
6709
- setState((current) => current ? previewHeaderLayout(current, id) : current);
6710
- }, []);
6711
- const handlePreviewHeaderStyle = useCallback((id) => {
6712
- if (!id) return;
6713
- setState((current) => current ? previewHeaderStyle(current, id) : current);
6714
- }, []);
6715
- const handlePreviewFooterLook = useCallback((id) => {
6716
- const plan = planSiteChromeLookSelection({ surface: "footer", lookId: id });
6717
- if (plan.kind !== "footer") return;
6718
- setState((current) => current ? previewFooterLook(current, plan.next.footerLookId) : current);
6719
- }, []);
6720
- const handlePreviewPalette = useCallback((id) => {
6721
- setState((current) => current ? previewPaletteVariant(current, id) : current);
6722
- }, []);
6723
- const handlePreviewTypographyPreset = useCallback((id) => {
6724
- setState((current) => current ? previewTypographyPreset(current, id) : current);
6725
- }, []);
6726
- const handleBodyFontOverride = useCallback((fontFamily) => {
6727
- setState((current) => current ? setTypographyBodyFontOverride(current, fontFamily) : current);
6728
- }, []);
6729
- const handleHeadingFontOverride = useCallback((fontFamily) => {
6730
- setState((current) => current ? setTypographyHeadingFontOverride(current, fontFamily) : current);
6731
- }, []);
6732
- const handleClearTypographyOverrides = useCallback(() => {
6733
- setState((current) => current ? clearTypographyFontOverrides(current) : current);
6734
- }, []);
6735
- const handleTokenOverride = useCallback((role, hex) => {
6736
- setState((current) => {
6737
- if (!current) return current;
6738
- const next = { ...current.previewed.paletteOverrides ?? {}, [role]: hex };
6739
- const parsed = paletteOverridesSchema.safeParse(next);
6740
- if (!parsed.success) return current;
6741
- return setPaletteOverrides(current, parsed.data);
6742
- });
6743
- }, []);
6744
- const handleResetOverrides = useCallback(() => {
6745
- setState((current) => current ? setPaletteOverrides(current, null) : current);
6746
- }, []);
6747
- const handleToggleRow = useCallback((row) => {
6748
- setState((current) => current ? toggleRow(current, row) : current);
6749
- }, []);
6750
- const handleCancel = useCallback(() => {
6751
- setState((current) => current ? cancelPreview(current) : current);
6752
- }, []);
6753
- const handleDismissWarnings = useCallback(() => {
6754
- setState((current) => current ? dismissWarnings(current) : current);
6755
- }, []);
6756
- const handleDismissNotices = useCallback(() => {
6757
- setState((current) => current ? dismissNotices(current) : current);
6758
- }, []);
6759
- const handleApply = useCallback(async () => {
6760
- const live = stateRef.current;
6761
- if (!live || !isPreviewingChange(live)) return;
6762
- const previewed = live.previewed;
6763
- const siteStyle = getCuratedSiteStyle(previewed.siteStyleId);
6764
- if (!siteStyle) return;
6765
- const compiled = planSiteStylePreviewChange({
6766
- siteStyle,
6767
- selection: previewed,
6768
- baseTheme: theme,
6769
- headerSelectionContext,
6770
- footerSelectionContext
6771
- });
6772
- const body = buildApplySiteStyleSelectionBody({
6773
- base: live.base,
6774
- previewed: {
6775
- ...previewed,
6776
- buttonPersonalityId: compiled.resolvedSelections.buttonPersonalityId,
6777
- paletteVariantId: compiled.resolvedSelections.paletteVariantId,
6778
- headerLayoutId: compiled.resolvedSelections.headerLayoutId,
6779
- headerStyleId: compiled.resolvedSelections.headerStyleId,
6780
- headerLookId: compiled.resolvedSelections.headerLookId,
6781
- typographyPresetId: compiled.resolvedSelections.typographyPresetId,
6782
- typographyBodyFontOverride: compiled.resolvedSelections.typographyBodyFontOverride,
6783
- typographyHeadingFontOverride: compiled.resolvedSelections.typographyHeadingFontOverride
6784
- },
6785
- selectionId
6786
- });
6787
- await runnerRef.current.run({
6788
- optimistic: () => {
6789
- applyStarted("style-configurator");
6790
- setState((current) => current ? markApplySaving(current) : current);
6791
- },
6792
- network: async () => {
6793
- const response = await apiClient({
6794
- endpoint: "applySiteStyleSelection",
6795
- params: { siteId },
6796
- body
6797
- });
6798
- return unwrapResponse(response);
6799
- },
6800
- onSuccess: (saved) => {
6801
- setState(
6802
- (current) => current ? markApplySaved(current, {
6803
- siteStyleId: previewed.siteStyleId,
6804
- selectionVersion: saved.selectionVersion,
6805
- selectionUpdatedAt: saved.selectionUpdatedAt ?? previewed.selectionUpdatedAt ?? null,
6806
- buttonPersonalityId: saved.buttonPersonalityId,
6807
- paletteVariantId: saved.paletteVariantId,
6808
- paletteOverrides: saved.paletteOverrides ?? null,
6809
- typographyPresetId: saved.typographyPresetId,
6810
- typographyBodyFontOverride: saved.typographyBodyFontOverride,
6811
- typographyHeadingFontOverride: saved.typographyHeadingFontOverride,
6812
- headerLookId: saved.headerLookId,
6813
- headerLayoutId: saved.headerLayoutId,
6814
- headerStyleId: saved.headerStyleId,
6815
- footerLookId: saved.footerLookId
6816
- }) : current
6817
- );
6818
- applySucceeded("style-configurator");
6819
- startRefreshTransition(() => {
6820
- router.refresh();
6821
- });
6822
- },
6823
- onError: (error) => {
6824
- applyFailed("style-configurator");
6825
- const applyError = classifyStyleApplyError(error);
6826
- if (applyError.kind === "stale") {
6827
- startRefreshTransition(() => {
6828
- router.refresh();
6829
- });
6830
- }
6831
- const message = applyError.message;
6832
- setState((current) => current ? markApplyFailed(current, message) : current);
6833
- }
6834
- });
6835
- }, [
6836
- apiClient,
6837
- footerSelectionContext,
6838
- headerSelectionContext,
6839
- applyFailed,
6840
- applyStarted,
6841
- applySucceeded,
6842
- router,
6843
- selectionId,
6844
- siteId,
6845
- startRefreshTransition,
6846
- theme
6847
- ]);
6848
- if (!state || !baseSnapshot) {
6849
- return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading design controls..." });
6850
- }
6851
- const applyAllowed = canApplySiteStyle(siteStyleAccess);
6852
- const previewingChange = isPreviewingChange(state);
6853
- const saving = state.saveStatus.kind === "saving" || isRefreshPending;
6854
- const currentSiteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
6855
- if (!currentSiteStyle) {
6856
- return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "This site uses a Site Style that cannot be configured here." });
6857
- }
6858
- const currentPersonality = findButtonPersonality(state.previewed.buttonPersonalityId);
6859
- const currentPaletteVariant = findPaletteVariant(state.previewed.paletteVariantId);
6860
- const currentTypographyPreset = state.previewed.typographyPresetId ? findTypographyPreset(state.previewed.typographyPresetId) : null;
6861
- const chromeLookModels = buildSiteChromeLookModels({
6862
- state,
6863
- siteStyle: currentSiteStyle,
6864
- headerSelectionContext,
6865
- footerSelectionContext
6866
- });
6867
- const currentHeaderLayout = getHeaderLayout(state.previewed.headerLayoutId);
6868
- const currentHeaderStyle = getHeaderStyle(state.previewed.headerStyleId);
6869
- const currentFooterLook = state.previewed.footerLookId ? findFooterLook(state.previewed.footerLookId) : findFooterLook(chromeLookModels.footer.renderedLookId);
6870
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
6871
- state.warnings.length > 0 ? /* @__PURE__ */ jsx(WarningNotice, { warnings: state.warnings, onDismiss: handleDismissWarnings }) : null,
6872
- state.notices.length > 0 ? /* @__PURE__ */ jsx(NoticeBanner, { notices: state.notices, onDismiss: handleDismissNotices }) : null,
6873
- /* @__PURE__ */ jsx(
6874
- SiteStyleRow,
6875
- {
6876
- expanded: state.expandedRow === "style",
6877
- onToggle: () => handleToggleRow("style"),
6878
- current: currentSiteStyle,
6879
- onPreview: handlePreviewSiteStyle,
6880
- disabled: saving
6881
- }
6882
- ),
6883
- /* @__PURE__ */ jsx(
6884
- SiteChromeLookRow,
6885
- {
6886
- label: "Header layout",
6887
- ariaLabel: "Header layout",
6888
- defaultDescription: "Choose the header structure.",
6889
- expanded: state.expandedRow === "header-layout",
6890
- onToggle: () => handleToggleRow("header-layout"),
6891
- currentId: state.previewed.headerLayoutId,
6892
- currentLabel: currentHeaderLayout?.label ?? state.previewed.headerLayoutId,
6893
- scopeDescription: "Changes the header structure on every page.",
6894
- options: chromeLookModels.headerLayout.options,
6895
- diagnostics: [],
6896
- onCycle: (direction) => {
6897
- const next = cycleSiteChromeLookId(chromeLookModels.headerLayout.options, state.previewed.headerLayoutId, direction);
6898
- if (next !== void 0) handlePreviewHeaderLayout(next);
6899
- },
6900
- onPreview: handlePreviewHeaderLayout,
6901
- disabled: saving
6902
- }
6903
- ),
6904
- /* @__PURE__ */ jsx(
6905
- SiteChromeLookRow,
6906
- {
6907
- label: "Header look",
6908
- ariaLabel: "Header look",
6909
- defaultDescription: "Choose the header visual style.",
6910
- expanded: state.expandedRow === "header-style",
6911
- onToggle: () => handleToggleRow("header-style"),
6912
- currentId: state.previewed.headerStyleId,
6913
- currentLabel: currentHeaderStyle?.label ?? state.previewed.headerStyleId,
6914
- scopeDescription: "Changes the header surface and link style.",
6915
- options: chromeLookModels.headerStyle.options,
6916
- diagnostics: [],
6917
- onCycle: (direction) => {
6918
- const next = cycleSiteChromeLookId(chromeLookModels.headerStyle.options, state.previewed.headerStyleId, direction);
6919
- if (next !== void 0) handlePreviewHeaderStyle(next);
6920
- },
6921
- onPreview: handlePreviewHeaderStyle,
6922
- disabled: saving
6923
- }
6924
- ),
6925
- /* @__PURE__ */ jsx(
6926
- SiteChromeLookRow,
6927
- {
6928
- label: "Footer",
6929
- ariaLabel: "Footer",
6930
- defaultDescription: "Use the footer chosen for this site style.",
6931
- catalog: footerLookCatalog,
6932
- expanded: state.expandedRow === "footer",
6933
- onToggle: () => handleToggleRow("footer"),
6934
- currentId: state.previewed.footerLookId,
6935
- currentLabel: state.previewed.footerLookId ? currentFooterLook?.label ?? "Unknown footer Look" : `${currentFooterLook?.label ?? "Site style"} (default)`,
6936
- scopeDescription: "Changes the footer on every page.",
6937
- options: chromeLookModels.footer.options,
6938
- diagnostics: chromeLookModels.footer.diagnostics,
6939
- onCycle: (direction) => {
6940
- const next = cycleSiteChromeLookId(chromeLookModels.footer.options, state.previewed.footerLookId, direction);
6941
- if (next !== void 0) handlePreviewFooterLook(next);
6942
- },
6943
- onPreview: handlePreviewFooterLook,
6944
- disabled: saving
6945
- }
6946
- ),
6947
- /* @__PURE__ */ jsx(
6948
- PersonalityRow,
6949
- {
6950
- expanded: state.expandedRow === "buttons",
6951
- onToggle: () => handleToggleRow("buttons"),
6952
- siteStyle: currentSiteStyle,
6953
- currentId: state.previewed.buttonPersonalityId,
6954
- currentLabel: currentPersonality?.name ?? state.previewed.buttonPersonalityId,
6955
- onPreview: handlePreviewPersonality,
6956
- disabled: saving
6957
- }
6958
- ),
6959
- /* @__PURE__ */ jsx(
6960
- TypographyRow,
6961
- {
6962
- expanded: state.expandedRow === "typography",
6963
- onToggle: () => handleToggleRow("typography"),
6964
- apiClient,
6965
- siteStyle: currentSiteStyle,
6966
- currentPresetId: state.previewed.typographyPresetId,
6967
- currentLabel: currentTypographyPreset?.name ?? "Custom typography",
6968
- bodyFontOverride: state.previewed.typographyBodyFontOverride,
6969
- headingFontOverride: state.previewed.typographyHeadingFontOverride,
6970
- onPreviewPreset: handlePreviewTypographyPreset,
6971
- onBodyFontOverride: handleBodyFontOverride,
6972
- onHeadingFontOverride: handleHeadingFontOverride,
6973
- onClearOverrides: handleClearTypographyOverrides,
6974
- disabled: saving
6975
- }
6976
- ),
6977
- /* @__PURE__ */ jsx(
6978
- PaletteRow,
6979
- {
6980
- expanded: state.expandedRow === "palette",
6981
- onToggle: () => handleToggleRow("palette"),
6982
- siteStyle: currentSiteStyle,
6983
- currentId: state.previewed.paletteVariantId,
6984
- currentLabel: currentPaletteVariant?.name ?? state.previewed.paletteVariantId,
6985
- currentSwatch: currentPaletteVariant?.colors.find((c) => c.name === "primary")?.hex ?? null,
6986
- onPreview: handlePreviewPalette,
6987
- disabled: saving
6988
- }
6989
- ),
6990
- currentPaletteVariant ? /* @__PURE__ */ jsx(
6991
- ColoursRow,
6992
- {
6993
- expanded: state.expandedRow === "colours",
6994
- onToggle: () => handleToggleRow("colours"),
6995
- variant: currentPaletteVariant,
6996
- overrides: state.previewed.paletteOverrides,
6997
- onTokenChange: handleTokenOverride,
6998
- onReset: handleResetOverrides,
6999
- disabled: saving
7000
- }
7001
- ) : null,
7002
- previewingChange ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pt-2", children: [
7003
- /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: "outline", onClick: handleCancel, disabled: saving, children: "Cancel" }),
7004
- applyAllowed ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: handleApply, disabled: saving, children: saving ? "Applying\u2026" : "Apply" }) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Preview only \u2014 ask an admin to apply." })
7005
- ] }) : null,
7006
- state.saveStatus.kind === "saved" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-success", children: "Saved." }) : null,
7007
- state.saveStatus.kind === "failed" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: state.saveStatus.message }) : null
7008
- ] });
7009
- }
7010
- function StyleRowFrame({ label, currentLabel, swatch, expanded, onToggle, children }) {
7011
- return /* @__PURE__ */ jsx(Collapsible, { open: expanded, onOpenChange: onToggle, children: /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-background", children: [
7012
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
7013
- "button",
7014
- {
7015
- type: "button",
7016
- className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-accent/30",
7017
- "aria-expanded": expanded,
7018
- children: [
7019
- /* @__PURE__ */ jsx(ChevronRight, { className: cn("h-4 w-4 transition", expanded && "rotate-90") }),
7020
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
7021
- /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: label }),
7022
- /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: currentLabel })
7023
- ] }),
7024
- swatch ? /* @__PURE__ */ jsx(
7025
- "span",
7026
- {
7027
- className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
7028
- style: { backgroundColor: swatch },
7029
- "aria-hidden": "true"
7030
- }
7031
- ) : null
7032
- ]
7033
- }
7034
- ) }),
7035
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "border-t px-3 py-3", children }) })
7036
- ] }) });
7037
- }
7038
- function SiteStyleRow({ expanded, onToggle, current, onPreview, disabled }) {
7039
- return /* @__PURE__ */ jsx(StyleRowFrame, { label: "Site style", currentLabel: current.name, expanded, onToggle, children: /* @__PURE__ */ jsx("ul", { role: "radiogroup", "aria-label": "Site style", className: "list-none space-y-1 p-0", children: curatedSiteStyles.map((style) => /* @__PURE__ */ jsx(
7040
- PickerOption,
7041
- {
7042
- selected: style.id === current.id,
7043
- label: style.name,
7044
- description: style.description,
7045
- onSelect: () => onPreview(style.id),
7046
- disabled
7047
- },
7048
- style.id
7049
- )) }) });
7050
- }
7051
- function PersonalityRow({ expanded, onToggle, siteStyle, currentId, currentLabel, onPreview, disabled }) {
7052
- const options = useMemo(
7053
- () => requireEntriesForCuratedChoices(buttonPersonalities, siteStyle.buttonPersonalityChoices),
7054
- [siteStyle.buttonPersonalityChoices]
7055
- );
7056
- return /* @__PURE__ */ jsx(StyleRowFrame, { label: "Button style", currentLabel, expanded, onToggle, children: /* @__PURE__ */ jsx("ul", { role: "radiogroup", "aria-label": "Button style", className: "list-none space-y-1 p-0", children: options.map((personality) => /* @__PURE__ */ jsx(
7057
- PickerOption,
7058
- {
7059
- selected: personality.id === currentId,
7060
- label: personality.name,
7061
- description: personality.description,
7062
- onSelect: () => onPreview(personality.id),
7063
- disabled
7064
- },
7065
- personality.id
7066
- )) }) });
7067
- }
7068
- function TypographyRow({
7069
- expanded,
7070
- onToggle,
7071
- apiClient,
7072
- siteStyle,
7073
- currentPresetId,
7074
- currentLabel,
7075
- bodyFontOverride,
7076
- headingFontOverride,
7077
- onPreviewPreset,
7078
- onBodyFontOverride,
7079
- onHeadingFontOverride,
7080
- onClearOverrides,
7081
- disabled
7082
- }) {
7083
- const options = useMemo(
7084
- () => requireEntriesForCuratedChoices(typographyPresets, siteStyle.typographyPresetChoices),
7085
- [siteStyle.typographyPresetChoices]
7086
- );
7087
- const fontCatalog = useFontCatalog(apiClient, expanded);
7088
- const selectedPreset = currentPresetId ? findTypographyPreset(currentPresetId) : null;
7089
- return /* @__PURE__ */ jsxs(StyleRowFrame, { label: "Typography", currentLabel, expanded, onToggle, children: [
7090
- currentPresetId === null ? /* @__PURE__ */ jsx("div", { className: "mb-3 rounded-md border border-muted-foreground/20 bg-muted/30 px-3 py-2 text-xs text-muted-foreground", children: "Current custom setup" }) : null,
7091
- /* @__PURE__ */ jsx("ul", { role: "radiogroup", "aria-label": "Typography preset", className: "space-y-1", children: options.map((preset) => /* @__PURE__ */ jsx(
7092
- TypographyPresetOption,
7093
- {
7094
- preset,
7095
- selected: preset.id === currentPresetId,
7096
- onSelect: () => onPreviewPreset(preset.id),
7097
- disabled
7098
- },
7099
- preset.id
7100
- )) }),
7101
- /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-3 border-t pt-3", children: [
7102
- selectedPreset ? /* @__PURE__ */ jsxs(Fragment, { children: [
7103
- /* @__PURE__ */ jsx(
7104
- TypographyFontSlotPicker,
7105
- {
7106
- label: "Body font",
7107
- slot: "body",
7108
- preset: selectedPreset,
7109
- catalog: fontCatalog,
7110
- value: bodyFontOverride,
7111
- onChange: onBodyFontOverride,
7112
- disabled
7113
- }
7114
- ),
7115
- /* @__PURE__ */ jsx(
7116
- TypographyFontSlotPicker,
7117
- {
7118
- label: "Heading font",
7119
- slot: "heading",
7120
- preset: selectedPreset,
7121
- catalog: fontCatalog,
7122
- value: headingFontOverride,
7123
- onChange: onHeadingFontOverride,
7124
- disabled
7125
- }
7126
- )
7127
- ] }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Choose a typography preset to use curated font overrides." }),
7128
- /* @__PURE__ */ jsx(
7129
- Button,
7130
- {
7131
- type: "button",
7132
- size: "sm",
7133
- variant: "outline",
7134
- onClick: onClearOverrides,
7135
- disabled: disabled || !bodyFontOverride && !headingFontOverride,
7136
- children: "Use preset fonts"
7137
- }
7138
- )
7139
- ] })
7140
- ] });
7141
- }
7142
- function TypographyPresetOption({
7143
- preset,
7144
- selected,
7145
- onSelect,
7146
- disabled
7147
- }) {
7148
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
7149
- "button",
7150
- {
7151
- type: "button",
7152
- role: "radio",
7153
- "aria-checked": selected,
7154
- onClick: onSelect,
7155
- disabled,
7156
- className: cn(
7157
- "w-full rounded-md border px-3 py-2 text-left transition disabled:cursor-not-allowed disabled:opacity-50",
7158
- selected ? "border-primary bg-primary/5" : "border-border hover:bg-muted/60"
7159
- ),
7160
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
7161
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7162
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: preset.name }),
7163
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: preset.description })
7164
- ] }),
7165
- /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
7166
- /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold uppercase tracking-wide", children: preset.defaultFonts.heading.split(",")[0] }),
7167
- /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: preset.defaultFonts.body.split(",")[0] })
7168
- ] })
7169
- ] })
7170
- }
7171
- ) });
7172
- }
7173
- function TypographyFontSlotPicker({
7174
- label,
7175
- slot,
7176
- preset,
7177
- catalog,
7178
- value,
7179
- onChange,
7180
- disabled
7181
- }) {
7182
- const [query, setQuery] = useState("");
7183
- const defaultFont = primaryFontFamily(preset.defaultFonts[slot]);
7184
- const selectedFamily = value ?? defaultFont;
7185
- const recordsByFamily = useMemo(() => indexFontsByFamily(catalog.fonts), [catalog.fonts]);
7186
- const selectedFit = evaluateTypographyFontSlot({
7187
- slot,
7188
- policy: preset.slotPolicies[slot],
7189
- capability: capabilityForFamily(selectedFamily, recordsByFamily)
7190
- });
7191
- const recommendedFamilies = preset.recommendedFonts[slot];
7192
- const recommendedOptions = useMemo(
7193
- () => uniqueFontFamilies(recommendedFamilies),
7194
- [recommendedFamilies]
7195
- );
7196
- const browseOptions = useMemo(() => {
7197
- const normalizedQuery = query.trim().toLowerCase();
7198
- const source = normalizedQuery.length > 0 ? catalog.fonts.filter((font) => font.family.toLowerCase().includes(normalizedQuery)) : catalog.fonts;
7199
- return [...source].sort((a, b) => sortFontForPresetSlot(a, b, slot, preset, recordsByFamily)).slice(0, normalizedQuery.length > 0 ? MAX_FONT_RESULTS_WHEN_SEARCHING : MAX_FONT_RESULTS_WHEN_BROWSING);
7200
- }, [catalog.fonts, preset, query, recordsByFamily, slot]);
7201
- return /* @__PURE__ */ jsxs("section", { className: "space-y-2", "aria-label": label, children: [
7202
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
7203
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7204
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }),
7205
- /* @__PURE__ */ jsx("div", { className: "truncate text-sm", children: value ? selectedFamily : `${defaultFont} (preset)` })
7206
- ] }),
7207
- /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit: selectedFit })
7208
- ] }),
7209
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-1", children: [
7210
- /* @__PURE__ */ jsxs(
7211
- "button",
7212
- {
7213
- type: "button",
7214
- className: cn(
7215
- "rounded-md border px-3 py-2 text-left text-sm transition disabled:cursor-not-allowed disabled:opacity-50",
7216
- value === null ? "border-foreground bg-accent/40" : "border-transparent hover:border-foreground/40 hover:bg-accent/20"
7217
- ),
7218
- onClick: () => onChange(null),
7219
- disabled,
7220
- children: [
7221
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Use preset font" }),
7222
- /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: defaultFont })
7223
- ]
7224
- }
7225
- ),
7226
- recommendedOptions.map((family) => /* @__PURE__ */ jsx(
7227
- FontFamilyOption,
7228
- {
7229
- family,
7230
- selected: value === family,
7231
- fit: evaluateTypographyFontSlot({
7232
- slot,
7233
- policy: preset.slotPolicies[slot],
7234
- capability: capabilityForFamily(family, recordsByFamily)
7235
- }),
7236
- onSelect: () => onChange(family),
7237
- disabled
7238
- },
7239
- family
7240
- ))
7241
- ] }),
7242
- /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border bg-muted/20 p-2", children: [
7243
- /* @__PURE__ */ jsx(
7244
- "input",
7245
- {
7246
- type: "search",
7247
- value: query,
7248
- placeholder: `Search ${slot} fonts`,
7249
- onChange: (event) => setQuery(event.currentTarget.value),
7250
- disabled: disabled || catalog.kind === "loading",
7251
- className: "h-8 w-full rounded-md border bg-background px-2 text-sm outline-none focus:border-primary disabled:cursor-not-allowed disabled:opacity-50"
7252
- }
7253
- ),
7254
- catalog.kind === "loading" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading font catalog..." }) : catalog.kind === "failed" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: catalog.message }) : null,
7255
- browseOptions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "max-h-56 space-y-1 overflow-y-auto pr-1", children: browseOptions.map((font) => /* @__PURE__ */ jsx(
7256
- FontFamilyOption,
7257
- {
7258
- family: font.family,
7259
- selected: value === font.family,
7260
- fit: evaluateTypographyFontSlot({
7261
- slot,
7262
- policy: preset.slotPolicies[slot],
7263
- capability: capabilityFromFontRecord(font)
7264
- }),
7265
- onSelect: () => onChange(font.family),
7266
- disabled
7267
- },
7268
- font.family
7269
- )) }) : catalog.kind === "loaded" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No matching Google Fonts." }) : null
7270
- ] }),
7271
- /* @__PURE__ */ jsxs("label", { className: "block space-y-1", children: [
7272
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Brand or uploaded font name" }),
7273
- /* @__PURE__ */ jsx(
7274
- "input",
7275
- {
7276
- type: "text",
7277
- value: value ?? "",
7278
- placeholder: "Enter any font family",
7279
- onChange: (event) => onChange(normalizeFontFamilyOverride(event.currentTarget.value)),
7280
- disabled,
7281
- className: "h-8 w-full rounded-md border bg-background px-2 text-sm outline-none focus:border-primary disabled:cursor-not-allowed disabled:opacity-50"
7282
- }
7283
- )
7284
- ] })
7285
- ] });
7286
- }
7287
- function FontFamilyOption({
7288
- family,
7289
- selected,
7290
- fit,
7291
- onSelect,
7292
- disabled
7293
- }) {
7294
- return /* @__PURE__ */ jsxs(
7295
- "button",
7296
- {
7297
- type: "button",
7298
- className: cn(
7299
- "flex w-full items-center justify-between gap-3 rounded-md border px-3 py-2 text-left text-sm transition disabled:cursor-not-allowed disabled:opacity-50",
7300
- selected ? "border-foreground bg-accent/40" : "border-transparent bg-background hover:border-foreground/40 hover:bg-accent/20"
7301
- ),
7302
- onClick: onSelect,
7303
- disabled,
7304
- children: [
7305
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: family }),
7306
- /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit })
7307
- ]
7308
- }
7309
- );
7310
- }
7311
- function TypographyFontFitBadge({ fit }) {
7312
- return /* @__PURE__ */ jsx(
7313
- "span",
7314
- {
7315
- className: cn(
7316
- "shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium",
7317
- fit.kind === "recommended" && "bg-emerald-100 text-emerald-800",
7318
- fit.kind === "compatible" && "bg-sky-100 text-sky-800",
7319
- fit.kind === "limited" && "bg-amber-100 text-amber-900",
7320
- fit.kind === "risky" && "bg-rose-100 text-rose-800",
7321
- fit.kind === "unknown" && "bg-muted text-muted-foreground"
7322
- ),
7323
- title: fit.reason,
7324
- children: typographyFontFitLabel(fit.kind)
7325
- }
7326
- );
7327
- }
7328
- function useFontCatalog(apiClient, enabled) {
7329
- const [catalog, setCatalog] = useState({ kind: "idle", fonts: [] });
7330
- const requestedClientRef = useRef(null);
7331
- useEffect(() => {
7332
- if (!enabled) return;
7333
- if (requestedClientRef.current === apiClient) return;
7334
- requestedClientRef.current = apiClient;
7335
- let cancelled = false;
7336
- setCatalog({ kind: "loading", fonts: [] });
7337
- async function loadFonts() {
7338
- try {
7339
- const response = await apiClient({ endpoint: "listFonts" });
7340
- const fonts = unwrapResponse(response);
7341
- if (!cancelled) {
7342
- setCatalog({
7343
- kind: "loaded",
7344
- fonts: Array.isArray(fonts) ? fonts : []
7345
- });
7346
- }
7347
- } catch (error) {
7348
- if (!cancelled) {
7349
- setCatalog({
7350
- kind: "failed",
7351
- fonts: [],
7352
- message: error instanceof Error ? error.message : "Unable to load the font catalog."
7353
- });
7354
- }
7355
- }
7356
- }
7357
- void loadFonts();
7358
- return () => {
7359
- cancelled = true;
7360
- };
7361
- }, [apiClient, enabled]);
7362
- return catalog;
7363
- }
7364
- function indexFontsByFamily(fonts) {
7365
- return new Map(fonts.map((font) => [font.family.toLowerCase(), font]));
7366
- }
7367
- function capabilityForFamily(family, recordsByFamily) {
7368
- const record = recordsByFamily.get(family.toLowerCase());
7369
- if (record) return capabilityFromFontRecord(record);
7370
- return {
7371
- source: "unknown",
7372
- family,
7373
- category: "unknown",
7374
- variants: []
7375
- };
7376
- }
7377
- function capabilityFromFontRecord(font) {
7378
- return {
7379
- source: "google",
7380
- family: font.family,
7381
- category: normalizeGoogleFontCategory(font.category),
7382
- variants: font.variants
7383
- };
7384
- }
7385
- function normalizeGoogleFontCategory(category) {
7386
- switch (category) {
7387
- case "serif":
7388
- case "sans-serif":
7389
- case "display":
7390
- case "handwriting":
7391
- case "monospace":
7392
- return category;
7393
- default:
7394
- return "unknown";
6445
+ if (typeof maxPopoverRows !== "number") {
6446
+ return cappedRequested;
7395
6447
  }
6448
+ const customizeRowCount = hasOverflowControls ? 1 : 0;
6449
+ const availableRows = maxPopoverRows - builtInPopoverRows - customizeRowCount;
6450
+ return Math.max(0, Math.min(cappedRequested, availableRows));
7396
6451
  }
7397
- function sortFontForPresetSlot(a, b, slot, preset, recordsByFamily) {
7398
- const aFit = evaluateTypographyFontSlot({
7399
- slot,
7400
- policy: preset.slotPolicies[slot],
7401
- capability: capabilityForFamily(a.family, recordsByFamily)
7402
- });
7403
- const bFit = evaluateTypographyFontSlot({
7404
- slot,
7405
- policy: preset.slotPolicies[slot],
7406
- capability: capabilityForFamily(b.family, recordsByFamily)
7407
- });
7408
- const fitDelta = typographyFontFitRank(aFit.kind) - typographyFontFitRank(bFit.kind);
7409
- if (fitDelta !== 0) return fitDelta;
7410
- const popularityDelta = (a.popularity ?? Infinity) - (b.popularity ?? Infinity);
7411
- if (popularityDelta !== 0) return popularityDelta;
7412
- return a.family.localeCompare(b.family);
7413
- }
7414
- function typographyFontFitRank(kind) {
7415
- switch (kind) {
7416
- case "recommended":
7417
- return 0;
7418
- case "compatible":
7419
- return 1;
7420
- case "limited":
7421
- return 2;
7422
- case "risky":
7423
- return 3;
7424
- case "unknown":
7425
- return 4;
7426
- default: {
7427
- const exhaustive = kind;
7428
- return exhaustive;
7429
- }
7430
- }
7431
- }
7432
- function typographyFontFitLabel(kind) {
7433
- switch (kind) {
7434
- case "recommended":
7435
- return "Recommended";
7436
- case "compatible":
7437
- return "Compatible";
7438
- case "limited":
7439
- return "Limited";
7440
- case "risky":
7441
- return "Risky";
7442
- case "unknown":
7443
- return "Unknown";
7444
- default: {
7445
- const exhaustive = kind;
7446
- return exhaustive;
7447
- }
7448
- }
7449
- }
7450
- function primaryFontFamily(cssFamily) {
7451
- return cssFamily.split(",")[0]?.trim().replace(/^["']|["']$/gu, "") || cssFamily;
7452
- }
7453
- function uniqueFontFamilies(families) {
7454
- const seen = /* @__PURE__ */ new Set();
7455
- const result = [];
7456
- for (const family of families) {
7457
- const normalized = primaryFontFamily(family);
7458
- const key = normalized.toLowerCase();
7459
- if (seen.has(key)) continue;
7460
- seen.add(key);
7461
- result.push(normalized);
7462
- }
7463
- return result;
7464
- }
7465
- function normalizeFontFamilyOverride(value) {
7466
- const trimmed = value.trim();
7467
- return trimmed.length > 0 ? trimmed : null;
7468
- }
7469
- function SiteChromeLookRow({
7470
- label,
7471
- ariaLabel,
7472
- defaultDescription,
7473
- catalog,
7474
- options = [
7475
- {
7476
- id: null,
7477
- label: "Site style default",
7478
- description: defaultDescription,
7479
- available: true
7480
- },
7481
- ...(catalog ?? []).map((look) => ({ ...look, available: true }))
7482
- ],
7483
- diagnostics,
7484
- scopeDescription,
7485
- expanded,
7486
- onToggle,
7487
- currentId,
7488
- currentLabel,
7489
- onCycle,
7490
- onPreview,
7491
- disabled
7492
- }) {
7493
- return /* @__PURE__ */ jsxs(StyleRowFrame, { label, currentLabel, expanded, onToggle, children: [
7494
- /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
7495
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: scopeDescription }),
7496
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
7497
- /* @__PURE__ */ jsx(
7498
- "button",
7499
- {
7500
- type: "button",
7501
- onClick: () => onCycle("prev"),
7502
- className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
7503
- "aria-label": `Previous ${label.toLowerCase()}`,
7504
- disabled: disabled || cycleableSiteChromeOptions(options).length <= 1,
7505
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
7506
- }
7507
- ),
7508
- /* @__PURE__ */ jsx(
7509
- "button",
7510
- {
7511
- type: "button",
7512
- onClick: () => onCycle("next"),
7513
- className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
7514
- "aria-label": `Next ${label.toLowerCase()}`,
7515
- disabled: disabled || cycleableSiteChromeOptions(options).length <= 1,
7516
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
7517
- }
7518
- )
7519
- ] })
7520
- ] }),
7521
- diagnostics.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mb-3 list-none space-y-1 p-0 text-xs text-amber-700", children: diagnostics.map((diagnostic) => /* @__PURE__ */ jsx("li", { children: diagnostic }, diagnostic)) }) : null,
7522
- /* @__PURE__ */ jsx("ul", { role: "radiogroup", "aria-label": ariaLabel, className: "list-none space-y-1 p-0", children: options.map((look) => /* @__PURE__ */ jsx(
7523
- PickerOption,
7524
- {
7525
- selected: look.id === currentId,
7526
- label: look.label,
7527
- description: look.description,
7528
- onSelect: () => onPreview(look.id),
7529
- disabled: disabled || !look.available
7530
- },
7531
- look.id ?? "site-style-default"
7532
- )) })
7533
- ] });
7534
- }
7535
- function PaletteRow({ expanded, onToggle, siteStyle, currentId, currentLabel, currentSwatch, onPreview, disabled }) {
7536
- const options = useMemo(
7537
- () => {
7538
- const recommended = requireEntriesForCuratedChoices(paletteVariants, siteStyle.paletteVariantChoices);
7539
- if (recommended.some((variant) => variant.id === currentId)) {
7540
- return recommended;
7541
- }
7542
- const current = findPaletteVariant(currentId);
7543
- return current ? [current, ...recommended] : recommended;
7544
- },
7545
- [currentId, siteStyle.paletteVariantChoices]
7546
- );
7547
- return /* @__PURE__ */ jsx(
7548
- StyleRowFrame,
7549
- {
7550
- label: "Palette",
7551
- currentLabel,
7552
- swatch: currentSwatch,
7553
- expanded,
7554
- onToggle,
7555
- children: /* @__PURE__ */ jsx("ul", { role: "radiogroup", "aria-label": "Palette", className: "list-none space-y-1 p-0", children: options.map((variant) => {
7556
- const primaryHex = variant.colors.find((c) => c.name === "primary")?.hex;
7557
- return /* @__PURE__ */ jsx(
7558
- PickerOption,
7559
- {
7560
- selected: variant.id === currentId,
7561
- label: variant.name,
7562
- description: variant.description,
7563
- swatch: primaryHex,
7564
- onSelect: () => onPreview(variant.id),
7565
- disabled
7566
- },
7567
- variant.id
7568
- );
7569
- }) })
7570
- }
7571
- );
7572
- }
7573
- function ColoursRow({ expanded, onToggle, variant, overrides, onTokenChange, onReset, disabled }) {
7574
- const mergedPalette = useMemo(() => {
7575
- return variant.colors.map((color) => ({
7576
- name: color.name,
7577
- hex: overrides?.[color.name] ?? color.hex
7578
- }));
7579
- }, [variant, overrides]);
7580
- const overrideCount = overrides ? Object.keys(overrides).length : 0;
7581
- const currentLabel = overrideCount > 0 ? `${overrideCount} colour${overrideCount === 1 ? "" : "s"} customised` : "Use variant colours";
7582
- return /* @__PURE__ */ jsx(
7583
- StyleRowFrame,
7584
- {
7585
- label: "Brand colours",
7586
- currentLabel,
7587
- expanded,
7588
- onToggle,
7589
- children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
7590
- /* @__PURE__ */ jsx(
7591
- PaletteTokenEditor,
7592
- {
7593
- palette: mergedPalette,
7594
- onChange: onTokenChange,
7595
- feedbackRoles: [],
7596
- surfaceRoles: [],
7597
- showContrastChecks: false
7598
- }
7599
- ),
7600
- overrideCount > 0 ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
7601
- Button,
7602
- {
7603
- type: "button",
7604
- variant: "outline",
7605
- size: "sm",
7606
- onClick: onReset,
7607
- disabled,
7608
- children: "Reset to variant"
7609
- }
7610
- ) }) : null
7611
- ] })
7612
- }
7613
- );
7614
- }
7615
- function PickerOption({ selected, label, description, swatch, onSelect, disabled }) {
7616
- return /* @__PURE__ */ jsx("li", { className: "list-none", children: /* @__PURE__ */ jsxs(
7617
- "button",
7618
- {
7619
- type: "button",
7620
- role: "radio",
7621
- "aria-checked": selected,
7622
- onClick: onSelect,
7623
- disabled,
7624
- className: cn(
7625
- "flex w-full items-center gap-3 rounded-md border px-3 py-2 text-left transition",
7626
- selected ? "border-foreground bg-accent/40 shadow-sm" : "border-transparent bg-background hover:border-foreground/40 hover:bg-accent/20",
7627
- disabled && "cursor-not-allowed opacity-60"
7628
- ),
7629
- children: [
7630
- swatch ? /* @__PURE__ */ jsx(
7631
- "span",
7632
- {
7633
- className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
7634
- style: { backgroundColor: swatch },
7635
- "aria-hidden": "true"
7636
- }
7637
- ) : null,
7638
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
7639
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: label }),
7640
- description ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: description }) : null
7641
- ] })
7642
- ]
7643
- }
7644
- ) });
7645
- }
7646
- function classifyStyleApplyError(error) {
7647
- if (isRecord(error)) {
7648
- const status = error.status;
7649
- const code = error.code;
7650
- if (status === 409 || code === "resource:conflict") {
7651
- return {
7652
- kind: "stale",
7653
- message: "This design changed elsewhere. Refreshing the latest version now."
7654
- };
7655
- }
7656
- }
7657
- return {
7658
- kind: "other",
7659
- message: error instanceof Error ? error.message : "Unable to apply your selection."
7660
- };
7661
- }
7662
- function isRecord(value) {
7663
- return typeof value === "object" && value !== null;
7664
- }
7665
- function WarningNotice({ warnings, onDismiss }) {
7666
- return /* @__PURE__ */ jsxs("div", { role: "status", className: "rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning-foreground", children: [
7667
- /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: warnings.map((warning, index) => /* @__PURE__ */ jsx("li", { children: describeWarning(warning) }, `${warning.kind}-${index}`)) }),
7668
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onDismiss, className: "mt-2 underline", children: "Dismiss" })
7669
- ] });
7670
- }
7671
- function NoticeBanner({ notices, onDismiss }) {
7672
- return /* @__PURE__ */ jsxs("div", { role: "status", className: "rounded-md border border-muted-foreground/20 bg-muted/40 px-3 py-2 text-xs", children: [
7673
- /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: notices.map((notice, index) => /* @__PURE__ */ jsx("li", { children: describeNotice(notice) }, `${notice.kind}-${index}`)) }),
7674
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onDismiss, className: "mt-2 underline", children: "Dismiss" })
7675
- ] });
7676
- }
7677
- function describeNotice(notice) {
7678
- switch (notice.kind) {
7679
- case "palette-overrides-hidden":
7680
- return "Custom colours are saved for the previous palette.";
7681
- case "palette-overrides-restored":
7682
- return "Custom colours restored for this palette.";
7683
- }
7684
- }
7685
- function describeWarning(warning) {
7686
- switch (warning.kind) {
7687
- case "rejected-personality":
7688
- return `Button style changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
7689
- case "rejected-palette":
7690
- return `Palette changed to ${warning.fellBackTo} because ${warning.requested} is not available.`;
7691
- case "rejected-typography-preset":
7692
- return `Typography changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
7693
- default: {
7694
- const exhaustive = warning;
7695
- return exhaustive;
7696
- }
7697
- }
7698
- }
7699
- function PreviewEditorSidebar({
7700
- surfaceId,
7701
- siteId,
7702
- dashboardBaseUrl = null,
7703
- pageId,
7704
- entryData,
7705
- contentTypeSchema,
7706
- referenceContentTypeKeys = [],
7707
- draftContent,
7708
- currentTemplate,
7709
- theme,
7710
- themeId = null,
7711
- themeSelectionId = null,
7712
- themeSelectionVersion = null,
7713
- themeSelectionUpdatedAt = null,
7714
- themeSelectionButtonPersonalityId = null,
7715
- themeSelectionPaletteVariantId = null,
7716
- themeSelectionPaletteOverrides = null,
7717
- themeSelectionHeaderLayoutId = null,
7718
- themeSelectionHeaderStyleId = null,
7719
- themeSelectionTypographyPresetId = null,
7720
- themeSelectionTypographyBodyFontOverride = null,
7721
- themeSelectionTypographyHeadingFontOverride = null,
7722
- themeSelectionHeaderLookId = null,
7723
- themeSelectionFooterLookId = null,
7724
- themeSelectionHeaderContext,
7725
- themeSelectionFooterContext,
7726
- designRowRequest = null,
7727
- apiClient,
7728
- bearerToken,
7729
- supabaseUrl,
7730
- entrySubrouteContext,
7731
- assistantPanel,
7732
- sdkConfig,
7733
- mediaAssets,
7734
- siteStyleAccess = { kind: "hidden" },
7735
- activeTab,
7736
- onTabChange,
7737
- onClose,
7738
- viewportMode,
7739
- viewportWidth,
7740
- zoom,
7741
- onViewportModeChange,
7742
- onViewportWidthChange,
7743
- onZoomChange
7744
- }) {
7745
- const controller = useEntryController();
7746
- const controllerBlocks = controller.blocks;
7747
- const uiStore = useEditorUiStoreInstance();
7748
- const uiStoreForBlockEditor = useMemo(
7749
- () => ({
7750
- getState: uiStore.getState,
7751
- subscribe: (listener) => uiStore.subscribe(() => listener())
7752
- }),
7753
- [uiStore]
7754
- );
7755
- const focusRequest = useUISelector(uiStore, (s) => s.focusRequest);
7756
- const { canEdit } = usePreviewAuth();
7757
- const dashboardSetupHandoff = useMemo(
7758
- () => resolvePreviewDashboardSetupHandoff(),
7759
- []
7760
- );
7761
- const dashboardContinueUrl = useMemo(
7762
- () => buildPreviewDashboardSetupUrl(dashboardBaseUrl, dashboardSetupHandoff),
7763
- [dashboardBaseUrl, dashboardSetupHandoff]
7764
- );
7765
- const sidebarChrome = useMemo(
7766
- () => resolvePreviewSidebarChromeModel(surfaceId),
7767
- [surfaceId]
7768
- );
7769
- const editorGuidance = useMemo(
7770
- () => resolvePreviewEditorGuidanceModel({
7771
- surfaceId,
7772
- canEdit,
7773
- hasDashboardSetupHandoff: Boolean(dashboardSetupHandoff)
7774
- }),
7775
- [canEdit, dashboardSetupHandoff, surfaceId]
7776
- );
7777
- const showAssistantTab = previewAssistantEnabled && Boolean(assistantPanel);
7778
- const assistantAvailable = showAssistantTab && Boolean(pageId && bearerToken && canEdit);
7779
- const effectiveActiveTab = showAssistantTab ? resolvePreviewSidebarTab(activeTab) : "editor";
7780
- const blockItems = useMemo(
7781
- () => controllerBlocks.list,
7782
- [controllerBlocks.list]
7783
- );
7784
- const blockEditorRef = useRef(null);
7785
- const contentFormWidgets = useMemo(() => previewWidgetOverrides, []);
7786
- const handleBlocksChange = useCallback(
7787
- (nextItems) => {
7788
- controllerBlocks.actions.setList(nextItems);
7789
- const drafts = uiStore.getState().drafts;
7790
- const extractData = (content) => {
7791
- if (!content || typeof content !== "object") return null;
7792
- const obj = content;
7793
- if ("data" in obj && obj.data && typeof obj.data === "object") {
7794
- return obj.data;
7795
- }
7796
- return obj;
7797
- };
7798
- for (const block of nextItems) {
7799
- if (!block.id) continue;
7800
- const baseline = extractData(
7801
- block.content ?? block.draftContent ?? block.publishedContent ?? null
7802
- );
7803
- const blockId = unsafeAsEditorBlockId(block.id);
7804
- if (drafts.getBlockSnapshot(blockId)) {
7805
- drafts.updateBlockBaseline(blockId, baseline, { kind: block.kind });
7806
- } else {
7807
- drafts.initNewBlock(blockId, baseline, { kind: block.kind });
7808
- }
7809
- }
7810
- },
7811
- [controllerBlocks.actions, uiStore]
7812
- );
7813
- useEffect(() => {
7814
- const req = focusRequest;
7815
- if (!req?.blockId) return;
7816
- blockEditorRef.current?.focusField({
7817
- blockId: req.blockId,
7818
- fieldPath: req.fieldPath ?? void 0,
7819
- itemIndex: req.itemIndex ?? void 0
7820
- });
7821
- uiStore.getState().clearFocus();
7822
- }, [focusRequest, uiStore]);
7823
- const metadataSectionContent = useMemo(() => {
7824
- if (!pageId && !entryData) return null;
7825
- return /* @__PURE__ */ jsx(PreviewMetadataPanel, { contentTypeKey: entryData?.type ?? null });
7826
- }, [entryData, pageId]);
7827
- const editorPanel = entryData ? /* @__PURE__ */ jsx(
7828
- EntryEditorPanel,
7829
- {
7830
- siteId,
7831
- entryData,
7832
- contentTypeSchema,
7833
- referenceContentTypeKeys,
7834
- draftContent,
7835
- currentTemplate,
7836
- theme,
7837
- apiClient,
7838
- bearerToken,
7839
- supabaseUrl,
7840
- entrySubrouteContext,
7841
- metadataPanel: metadataSectionContent,
7842
- mediaAssets,
7843
- widgets: contentFormWidgets,
7844
- viewportMode,
7845
- viewportWidth,
7846
- zoom,
7847
- onViewportModeChange,
7848
- onViewportWidthChange,
7849
- onZoomChange
7850
- }
7851
- ) : pageId ? /* @__PURE__ */ jsx(
7852
- PageBlockEditorPanel,
7853
- {
7854
- siteId,
7855
- pageId,
7856
- theme,
7857
- apiClient,
7858
- bearerToken,
7859
- supabaseUrl,
7860
- blockItems,
7861
- uiStoreForBlockEditor,
7862
- blockEditorRef,
7863
- onBlocksChange: handleBlocksChange,
7864
- metadataPanel: metadataSectionContent,
7865
- designPanel: canInspectSiteStyle(siteStyleAccess) ? /* @__PURE__ */ jsx(
7866
- StyleConfigurator,
7867
- {
7868
- siteId,
7869
- apiClient,
7870
- theme,
7871
- themeId,
7872
- selectionId: themeSelectionId,
7873
- selectionVersion: themeSelectionVersion,
7874
- selectionUpdatedAt: themeSelectionUpdatedAt,
7875
- initialSelections: {
7876
- selectionVersion: themeSelectionVersion,
7877
- selectionUpdatedAt: themeSelectionUpdatedAt,
7878
- buttonPersonalityId: themeSelectionButtonPersonalityId,
7879
- paletteVariantId: themeSelectionPaletteVariantId,
7880
- paletteOverrides: themeSelectionPaletteOverrides,
7881
- headerLayoutId: themeSelectionHeaderLayoutId,
7882
- headerStyleId: themeSelectionHeaderStyleId,
7883
- typographyPresetId: themeSelectionTypographyPresetId,
7884
- typographyBodyFontOverride: themeSelectionTypographyBodyFontOverride,
7885
- typographyHeadingFontOverride: themeSelectionTypographyHeadingFontOverride,
7886
- headerLookId: themeSelectionHeaderLookId,
7887
- footerLookId: themeSelectionFooterLookId
7888
- },
7889
- headerSelectionContext: themeSelectionHeaderContext,
7890
- footerSelectionContext: themeSelectionFooterContext,
7891
- expandedRowRequest: designRowRequest,
7892
- siteStyleAccess
7893
- }
7894
- ) : null,
7895
- sdkConfig: sdkConfig ?? null,
7896
- mediaAssets,
7897
- viewportMode,
7898
- viewportWidth,
7899
- zoom,
7900
- onViewportModeChange,
7901
- onViewportWidthChange,
7902
- onZoomChange
7903
- }
7904
- ) : /* @__PURE__ */ jsx("div", { className: "flex h-full flex-col", children: /* @__PURE__ */ jsx("div", { className: "flex-1 p-4", children: /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed px-4 py-6 text-sm text-muted-foreground", children: "Block editing is only available when a page is selected." }) }) });
7905
- const assistantPanelDisplay = assistantAvailable ? assistantPanel : /* @__PURE__ */ jsx("div", { className: "flex h-full flex-col items-center justify-center p-6 text-center text-sm text-muted-foreground", children: "AI Assistant is available for pages in preview mode." });
7906
- return /* @__PURE__ */ jsx(
7907
- "div",
7908
- {
7909
- className: `flex h-full min-h-0 flex-col overflow-hidden pb-[calc(var(--builder-editor-chrome-offset,0px)+var(--builder-preview-banner-offset,0px))] ${sidebarChrome.rootClassName}`,
7910
- "data-preview-sidebar-tone": sidebarChrome.tone,
7911
- children: /* @__PURE__ */ jsxs(
7912
- Tabs,
7913
- {
7914
- value: effectiveActiveTab,
7915
- onValueChange: (value) => onTabChange(
7916
- resolvePreviewSidebarTab(value)
7917
- ),
7918
- className: "flex h-full min-h-0 flex-col overflow-hidden",
7919
- children: [
7920
- /* @__PURE__ */ jsxs(
7921
- "div",
7922
- {
7923
- className: `shrink-0 flex items-center justify-between px-4 py-3 ${sidebarChrome.headerClassName}`,
7924
- children: [
7925
- /* @__PURE__ */ jsxs(TabsList, { className: "h-8", children: [
7926
- /* @__PURE__ */ jsx(TabsTrigger, { value: "editor", children: "Editor" }),
7927
- showAssistantTab ? /* @__PURE__ */ jsx(TabsTrigger, { value: "assistant", children: "AI Assistant" }) : null
7928
- ] }),
7929
- /* @__PURE__ */ jsx(
7930
- Button,
7931
- {
7932
- variant: "ghost",
7933
- size: "icon",
7934
- onClick: onClose,
7935
- "aria-label": "Close sidebar",
7936
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
7937
- }
7938
- )
7939
- ]
7940
- }
7941
- ),
7942
- editorGuidance ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-3", children: /* @__PURE__ */ jsx(
7943
- PreviewEditorGuidanceCard,
7944
- {
7945
- guidance: editorGuidance,
7946
- completeSetupDisabled: !dashboardContinueUrl,
7947
- onCompleteSetup: () => {
7948
- if (!dashboardContinueUrl || typeof window === "undefined")
7949
- return;
7950
- window.location.href = dashboardContinueUrl;
7951
- }
7952
- }
7953
- ) }) : null,
7954
- /* @__PURE__ */ jsx(
7955
- TabsContent,
7956
- {
7957
- value: "editor",
7958
- forceMount: true,
7959
- className: "mt-0 min-h-0 flex-1 overflow-hidden data-[state=inactive]:hidden",
7960
- children: editorPanel
7961
- }
7962
- ),
7963
- showAssistantTab ? /* @__PURE__ */ jsx(
7964
- TabsContent,
7965
- {
7966
- value: "assistant",
7967
- forceMount: true,
7968
- className: "mt-0 min-h-0 flex-1 overflow-hidden data-[state=inactive]:hidden",
7969
- children: assistantPanelDisplay
7970
- }
7971
- ) : null
7972
- ]
7973
- }
7974
- )
7975
- }
7976
- );
7977
- }
7978
- function PreviewMetadataPanel({
7979
- contentTypeKey
7980
- }) {
7981
- const uiStore = useEditorUiStoreInstance();
7982
- const manifest = useMemo(() => {
7983
- return omitRouteTitleFieldFromManifest(
7984
- getRouteMetadataManifestForType(contentTypeKey)
7985
- );
7986
- }, [contentTypeKey]);
7987
- const validation = useMemo(
7988
- () => createManifestValidation(manifest),
7989
- [manifest]
7990
- );
7991
- const backend = useMemo(
7992
- () => createStoreBackend({
7993
- scope: "metadata",
7994
- store: uiStore,
7995
- validateFn: (values) => {
7996
- const result = validation.schema.safeParse(values);
7997
- if (!result.success) {
7998
- return result.error.issues;
7999
- }
8000
- return [];
8001
- }
8002
- }),
8003
- [uiStore, validation]
8004
- );
8005
- return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
8006
- ManifestForm,
8007
- {
8008
- manifest,
8009
- backend,
8010
- className: "space-y-4"
8011
- }
8012
- ) });
6452
+ function resolveBlockManifest(blockKind, sdkConfig) {
6453
+ return getBlockManifestFromSdkConfig(blockKind, sdkConfig) ?? getBlockDefinition(blockKind)?.manifest ?? null;
8013
6454
  }
8014
6455
 
8015
- export { PreviewEditorSidebar };
6456
+ export { BUILT_IN_REFERENCE_KINDS, BlockEditor, ButtonVariantField, ButtonWidgetProvider, EntryPickerField, EntryPickerWidgetProvider, FileField, IconPickerField, LinkField, LinkWidgetProvider, MAX_CUSTOMIZE_POPOVER_ROWS, MAX_QUICK_MANIFEST_FIELDS, MediaField, PreviewBlockCustomizeSelectionProvider, ReferenceField, ReferenceWidgetProvider, RichTextField, RichTextWidgetProvider, SiteOptionPickerField, SiteOptionWidgetProvider, buildPreviewDashboardSetupUrl, canApplySiteStyle, canInspectSiteStyle, createSiteOptionApiLoader, createSiteOptionWidgetAdapters, mapContentSchemaToFormFields, parseReferenceKind, resolvePreviewDashboardSetupHandoff, resolvePreviewEditorGuidanceModel, resolvePreviewSetupBannerModel, resolvePreviewSidebarChromeModel, resolveSiteStyleAccess, sanitizeDashboardPath, useBlockCustomizeControls, usePreviewBlockCustomizeSelectionOptional, useRichTextImagePicker };