@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
@@ -0,0 +1,3163 @@
1
+ "use client";
2
+ import { resolvePreviewDashboardSetupHandoff, buildPreviewDashboardSetupUrl, resolvePreviewSidebarChromeModel, resolvePreviewEditorGuidanceModel, SiteOptionPickerField, ButtonVariantField, IconPickerField, EntryPickerField, ReferenceField, RichTextField, MediaField, FileField, LinkField, canInspectSiteStyle, mapContentSchemaToFormFields, usePreviewBlockCustomizeSelectionOptional, BlockEditor, canApplySiteStyle, LinkWidgetProvider, ReferenceWidgetProvider, EntryPickerWidgetProvider, SiteOptionWidgetProvider, ButtonWidgetProvider, createSiteOptionWidgetAdapters, createSiteOptionApiLoader, BUILT_IN_REFERENCE_KINDS, useRichTextImagePicker, RichTextWidgetProvider, useBlockCustomizeControls, MAX_QUICK_MANIFEST_FIELDS, parseReferenceKind } from './chunk-3QWQ2MUQ.mjs';
3
+ import { unwrapResponse, useEntryController, useEditorUiStoreInstance, useUISelector, resolvePreviewSidebarTab, BackgroundGradientField, BackgroundColorField, unsafeAsEditorBlockId, Tabs, TabsList, TabsTrigger, Button, X, TabsContent, useEditorUiStore, deriveRouteMetadataPresentation, resolvePageRouteSlug, createManifestValidation, createStoreBackend, ManifestForm, previewAssistantEnabled, Collapsible, CollapsibleTrigger, ChevronRight, CollapsibleContent, ContentFieldsForm, usePreviewDesignCoordinator, useSiteChromeCustomizeOptional, resolveStyleConfiguratorInitialSnapshot, createStyleConfiguratorState, isPreviewingChange, createOptimisticActionRunner, syncStyleConfiguratorState, getCuratedSiteStyle, planSiteStylePreviewChange, openRow, previewSiteStyle, applyResolvedSelections, previewButtonPersonality, previewHeaderLayout, previewHeaderStyle, planSiteChromeLookSelection, previewFooterLook, previewPaletteVariant, previewTypographyPreset, setTypographyBodyFontOverride, setTypographyHeadingFontOverride, clearTypographyFontOverrides, paletteOverridesSchema, setPaletteOverrides, toggleRow, cancelPreview, dismissWarnings, dismissNotices, deriveStyleConfiguratorCapabilities, buildApplySiteStyleSelectionBody, markApplyFailed, selectionSnapshotFromApplySiteStyleSelectionResponse, styleConfiguratorHistoryEntryForPersistedSelection, markApplySaved, markApplySaving, findButtonPersonality, findPaletteVariant, findTypographyPreset, buildSiteChromeLookModels, getHeaderLayout, getHeaderStyle, findFooterLook, cycleSiteChromeLookId, footerLookCatalog, resolveContentTitle, foldSubroute, SUBROUTE_KINDS, Smartphone, Tablet, Monitor, Maximize2, cn, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, expectParsedBrandedId, parseSiteId, buildThemeRuntime, SdkConfigProvider, PaletteProvider, MediaEditorProvider, curatedSiteStyles, ChevronLeft, cycleableSiteChromeOptions, requireEntriesForCuratedChoices, buttonPersonalities, typographyPresets, paletteVariants, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, FormRenderer, evaluateTypographyFontSlot, Callout, CalloutDescription, Separator, Surface, ColorPicker, Input } from './chunk-WUHKQBB6.mjs';
4
+ import { usePreviewAuth, getDashboardUrl } from './chunk-E3NZYEKF.mjs';
5
+ import './chunk-R7POPVJR.mjs';
6
+ import { useMemo, useRef, useCallback, useEffect, useState, useTransition, useSyncExternalStore, useId } from 'react';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import { useRouter } from 'next/navigation';
9
+
10
+ // ../editor-blocks/src/widgets/entry-picker/loaders.ts
11
+ function getStatusBadgeVariant(status) {
12
+ switch (status) {
13
+ case "published":
14
+ return "default";
15
+ case "draft":
16
+ return "secondary";
17
+ case "scheduled":
18
+ return "outline";
19
+ case "archived":
20
+ return "destructive";
21
+ default:
22
+ return "secondary";
23
+ }
24
+ }
25
+ function mapEntryPickerItems(entries, valueFrom) {
26
+ return entries.map((entry) => ({
27
+ value: valueFrom === "identifier" ? entry.identifier ?? entry.id : valueFrom === "slug" ? entry.slug ?? entry.id : entry.id,
28
+ label: entry.draftTitle || entry.title || entry.slug || entry.id,
29
+ id: entry.id,
30
+ identifier: entry.identifier ?? null,
31
+ slug: entry.slug ?? null,
32
+ badge: {
33
+ label: entry.status,
34
+ variant: getStatusBadgeVariant(entry.status)
35
+ }
36
+ }));
37
+ }
38
+ async function loadEntryPickerItems({
39
+ apiClient,
40
+ siteId,
41
+ contentType,
42
+ valueFrom
43
+ }) {
44
+ const result = await apiClient({
45
+ endpoint: "listContentEntries",
46
+ params: { siteId, type: contentType }
47
+ });
48
+ const { entries } = unwrapResponse(result);
49
+ return mapEntryPickerItems(entries, valueFrom);
50
+ }
51
+
52
+ // ../editor-blocks/src/widgets/link/loaders.ts
53
+ async function loadRoutableContentItems({
54
+ apiClient,
55
+ siteId
56
+ }) {
57
+ const result = await apiClient({
58
+ endpoint: "getRoutableContent",
59
+ params: { siteId }
60
+ });
61
+ const payload = unwrapResponse(result);
62
+ return Array.isArray(payload?.items) ? payload.items : [];
63
+ }
64
+
65
+ // ../editor-blocks/src/widgets/reference/dynamicAdapters.ts
66
+ function createDynamicReferenceAdapters(staticAdapters, createDynamicContentAdapter) {
67
+ return new Proxy(staticAdapters, {
68
+ get(target, prop) {
69
+ if (typeof prop !== "string") {
70
+ return void 0;
71
+ }
72
+ if (prop in target) {
73
+ return target[prop];
74
+ }
75
+ const parsed = parseReferenceKind(prop);
76
+ if (parsed.kind === "dynamicContent") {
77
+ return createDynamicContentAdapter(parsed.contentTypeKey);
78
+ }
79
+ return void 0;
80
+ }
81
+ });
82
+ }
83
+
84
+ // ../editor-blocks/src/widgets/reference/loaders.ts
85
+ function defineReferenceLoader(referenceKind, loader) {
86
+ return Object.assign(loader, { referenceKind });
87
+ }
88
+ function formatStatusBadge(status) {
89
+ if (status === "published") {
90
+ return { label: "Published", variant: "default" };
91
+ }
92
+ if (status === "scheduled") {
93
+ return { label: "Scheduled", variant: "secondary" };
94
+ }
95
+ if (status === "archived") {
96
+ return { label: "Archived", variant: "outline" };
97
+ }
98
+ return { label: "Draft", variant: "outline" };
99
+ }
100
+ function hasSlug(entry) {
101
+ return entry.slug !== null;
102
+ }
103
+ var loadPostReferenceItems = defineReferenceLoader("post", async ({ apiClient, siteId }) => {
104
+ const result = await apiClient({
105
+ endpoint: "listContentEntries",
106
+ params: { siteId, type: "post" }
107
+ });
108
+ const { entries } = unwrapResponse(result);
109
+ return entries.filter(hasSlug).filter((entry) => entry.status === "published").map((entry) => ({
110
+ value: entry.slug,
111
+ label: entry.title || entry.slug || entry.id,
112
+ badge: formatStatusBadge(entry.status)
113
+ }));
114
+ });
115
+ var loadFormReferenceItems = defineReferenceLoader("form", async ({ apiClient, siteId }) => {
116
+ const result = await apiClient({
117
+ endpoint: "listForms",
118
+ params: { siteId }
119
+ });
120
+ const { forms } = unwrapResponse(result);
121
+ return forms.map((form) => ({
122
+ value: form.slug,
123
+ label: form.name
124
+ }));
125
+ });
126
+ var loadBookingFormReferenceItems = defineReferenceLoader("bookingForm", async ({ apiClient, siteId }) => {
127
+ const result = await apiClient({
128
+ endpoint: "listBookingForms",
129
+ params: { siteId }
130
+ });
131
+ const { forms } = unwrapResponse(result);
132
+ return forms.map((form) => ({
133
+ value: form.id,
134
+ label: form.name
135
+ }));
136
+ });
137
+ var loadAppointmentServiceReferenceItems = defineReferenceLoader(
138
+ "appointmentService",
139
+ async ({ apiClient, siteId }) => {
140
+ const result = await apiClient({
141
+ endpoint: "getAppointmentServicesReference",
142
+ params: { siteId }
143
+ });
144
+ const { options } = unwrapResponse(result);
145
+ return options.map((option) => ({
146
+ value: option.id,
147
+ label: option.label
148
+ }));
149
+ }
150
+ );
151
+ var loadAppointmentResourceReferenceItems = defineReferenceLoader(
152
+ "appointmentResource",
153
+ async ({ apiClient, siteId }) => {
154
+ const result = await apiClient({
155
+ endpoint: "getAppointmentResourcesReference",
156
+ params: { siteId }
157
+ });
158
+ const { options } = unwrapResponse(result);
159
+ return options.map((option) => ({
160
+ value: option.id,
161
+ label: option.label
162
+ }));
163
+ }
164
+ );
165
+ var loadPageReferenceItems = defineReferenceLoader("page", async ({ apiClient, siteId }) => {
166
+ const result = await apiClient({
167
+ endpoint: "getRoutableContent",
168
+ params: { siteId }
169
+ });
170
+ const { items } = unwrapResponse(result);
171
+ return items.filter((item) => item.entityType === "page").map((page) => ({
172
+ value: page.entityId,
173
+ label: page.title || "Untitled"
174
+ }));
175
+ });
176
+ var loadNewsletterListReferenceItems = defineReferenceLoader(
177
+ "newsletterList",
178
+ async ({ apiClient, siteId }) => {
179
+ const result = await apiClient({
180
+ endpoint: "listNewsletterLists",
181
+ params: { siteId }
182
+ });
183
+ const { lists } = unwrapResponse(result);
184
+ return lists.map((list) => ({
185
+ value: list.id,
186
+ label: list.name
187
+ }));
188
+ }
189
+ );
190
+ async function loadDynamicContentReferenceItems({
191
+ apiClient,
192
+ siteId,
193
+ contentTypeKey
194
+ }) {
195
+ const result = await apiClient({
196
+ endpoint: "listContentEntries",
197
+ params: { siteId, type: contentTypeKey }
198
+ });
199
+ const { entries } = unwrapResponse(result);
200
+ return entries.filter((entry) => entry.status === "published").map((entry) => ({
201
+ value: entry.id,
202
+ label: entry.title || entry.slug || entry.id,
203
+ badge: { label: "Published", variant: "default" }
204
+ }));
205
+ }
206
+ var referenceLoaders = {
207
+ post: loadPostReferenceItems,
208
+ form: loadFormReferenceItems,
209
+ bookingForm: loadBookingFormReferenceItems,
210
+ appointmentService: loadAppointmentServiceReferenceItems,
211
+ appointmentResource: loadAppointmentResourceReferenceItems,
212
+ page: loadPageReferenceItems,
213
+ newsletterList: loadNewsletterListReferenceItems
214
+ };
215
+
216
+ // ../preview-next/src/client/widgets/widgetAdapters.ts
217
+ var routableContentCache = /* @__PURE__ */ new Map();
218
+ var entryPickerCache = /* @__PURE__ */ new Map();
219
+ var siteOptionItemsCache = /* @__PURE__ */ new Map();
220
+ var referenceItemsCache = /* @__PURE__ */ new Map();
221
+ var dynamicReferenceItemsCache = /* @__PURE__ */ new Map();
222
+ var MAX_CACHE_ENTRIES = 50;
223
+ function setBoundedCache(map, key, value) {
224
+ if (!map.has(key) && map.size >= MAX_CACHE_ENTRIES) {
225
+ const firstKey = map.keys().next().value;
226
+ if (firstKey !== void 0) {
227
+ map.delete(firstKey);
228
+ }
229
+ }
230
+ map.set(key, value);
231
+ }
232
+ function createCachedDynamicContentReferenceAdapter(contentTypeKey, previewClient, fallbackSiteId) {
233
+ return {
234
+ labels: {
235
+ loading: "Loading entries\u2026",
236
+ select: "Select an entry",
237
+ empty: "No entries found",
238
+ emptySelection: "Select an entry to continue."
239
+ },
240
+ loadItems: async ({ siteId }) => {
241
+ const resolvedSiteId = siteId || fallbackSiteId;
242
+ if (!resolvedSiteId) {
243
+ return [];
244
+ }
245
+ const cacheKey = `${resolvedSiteId}:content:${contentTypeKey}`;
246
+ const cached = dynamicReferenceItemsCache.get(cacheKey);
247
+ if (cached) return cached;
248
+ const promise = (async () => {
249
+ try {
250
+ return await loadDynamicContentReferenceItems({
251
+ apiClient: previewClient,
252
+ siteId: resolvedSiteId,
253
+ contentTypeKey
254
+ });
255
+ } catch (error) {
256
+ dynamicReferenceItemsCache.delete(cacheKey);
257
+ throw error;
258
+ }
259
+ })();
260
+ setBoundedCache(dynamicReferenceItemsCache, cacheKey, promise);
261
+ return promise;
262
+ }
263
+ };
264
+ }
265
+ function createCachedReferenceAdapterLoader(kind, loader, previewClient, fallbackSiteId) {
266
+ return async ({ siteId }) => {
267
+ const resolvedSiteId = siteId || fallbackSiteId;
268
+ if (!resolvedSiteId) {
269
+ return [];
270
+ }
271
+ const cacheKey = `${kind}:${resolvedSiteId}`;
272
+ const cached = referenceItemsCache.get(cacheKey);
273
+ if (cached) return cached;
274
+ const promise = (async () => {
275
+ try {
276
+ return await loader({ apiClient: previewClient, siteId: resolvedSiteId });
277
+ } catch (error) {
278
+ referenceItemsCache.delete(cacheKey);
279
+ throw error;
280
+ }
281
+ })();
282
+ setBoundedCache(referenceItemsCache, cacheKey, promise);
283
+ return promise;
284
+ };
285
+ }
286
+ function createPreviewLinkAdapters(previewClient) {
287
+ return {
288
+ fetchRoutableContent: async (currentSiteId) => {
289
+ const cached = routableContentCache.get(currentSiteId);
290
+ if (cached) return cached;
291
+ const promise = (async () => {
292
+ try {
293
+ return await loadRoutableContentItems({ apiClient: previewClient, siteId: currentSiteId });
294
+ } catch (error) {
295
+ routableContentCache.delete(currentSiteId);
296
+ throw error;
297
+ }
298
+ })();
299
+ setBoundedCache(routableContentCache, currentSiteId, promise);
300
+ return promise;
301
+ }
302
+ };
303
+ }
304
+ function createPreviewEntryPickerAdapter(previewClient) {
305
+ return {
306
+ loadItems: async ({ siteId, contentType, valueFrom }) => {
307
+ if (!siteId || !contentType) return [];
308
+ const cacheKey = `${siteId}:${contentType}:${valueFrom ?? "id"}`;
309
+ const cached = entryPickerCache.get(cacheKey);
310
+ if (cached) return cached;
311
+ const request = (async () => {
312
+ try {
313
+ return await loadEntryPickerItems({
314
+ apiClient: previewClient,
315
+ siteId,
316
+ contentType,
317
+ valueFrom
318
+ });
319
+ } catch (error) {
320
+ entryPickerCache.delete(cacheKey);
321
+ throw error;
322
+ }
323
+ })();
324
+ setBoundedCache(entryPickerCache, cacheKey, request);
325
+ return request;
326
+ },
327
+ labels: {
328
+ loading: "Loading entries...",
329
+ select: "Select an entry",
330
+ empty: "No entries found",
331
+ emptySelection: "Select an entry to embed.",
332
+ noContentType: "Select a content type first."
333
+ }
334
+ };
335
+ }
336
+ function createCachedSiteOptionLoader(loadItems) {
337
+ return async ({ siteId, source }) => {
338
+ const cacheKey = `${source}:${siteId}`;
339
+ const cached = siteOptionItemsCache.get(cacheKey);
340
+ if (cached) return cached;
341
+ const promise = (async () => {
342
+ try {
343
+ return await loadItems({ siteId, source });
344
+ } catch (error) {
345
+ siteOptionItemsCache.delete(cacheKey);
346
+ throw error;
347
+ }
348
+ })();
349
+ setBoundedCache(siteOptionItemsCache, cacheKey, promise);
350
+ return promise;
351
+ };
352
+ }
353
+ function createPreviewSiteOptionAdapters(previewClient) {
354
+ return createSiteOptionWidgetAdapters({
355
+ loadItems: createCachedSiteOptionLoader(createSiteOptionApiLoader(previewClient))
356
+ });
357
+ }
358
+ var previewReferenceChrome = {
359
+ post: {
360
+ labels: {
361
+ loading: "Loading posts\u2026",
362
+ select: "Select a post",
363
+ empty: "No posts found",
364
+ emptySelection: "Select a post to continue."
365
+ }
366
+ },
367
+ form: {
368
+ labels: {
369
+ loading: "Loading forms\u2026",
370
+ select: "Select a form",
371
+ empty: "No forms found",
372
+ emptySelection: "Select a form to continue."
373
+ }
374
+ },
375
+ bookingForm: {
376
+ labels: {
377
+ loading: "Loading booking forms\u2026",
378
+ select: "Select a booking form",
379
+ empty: "No booking forms yet",
380
+ emptySelection: "Select a booking form to enable appointment booking."
381
+ }
382
+ },
383
+ appointmentService: {
384
+ labels: {
385
+ loading: "Loading appointment services\u2026",
386
+ select: "Select a service",
387
+ empty: "No services available",
388
+ emptySelection: "Select a service to enable booking."
389
+ }
390
+ },
391
+ appointmentResource: {
392
+ labels: {
393
+ loading: "Loading resources\u2026",
394
+ select: "Select a resource",
395
+ empty: "No resources available",
396
+ emptySelection: "Select a resource (optional)."
397
+ }
398
+ },
399
+ page: {
400
+ labels: {
401
+ loading: "Loading pages\u2026",
402
+ select: "Select a page",
403
+ empty: "No pages available",
404
+ emptySelection: "Select a page."
405
+ }
406
+ },
407
+ newsletterList: {
408
+ labels: {
409
+ loading: "Loading lists\u2026",
410
+ select: "Select a list",
411
+ empty: "No lists yet",
412
+ emptySelection: "Select a newsletter list to subscribe users to."
413
+ }
414
+ }
415
+ };
416
+ function createPreviewReferenceAdapters(previewClient, fallbackSiteId) {
417
+ const brandedFallbackSiteId = void 0;
418
+ const adapters = {};
419
+ for (const kind of BUILT_IN_REFERENCE_KINDS) {
420
+ adapters[kind] = {
421
+ loadItems: createCachedReferenceAdapterLoader(
422
+ kind,
423
+ referenceLoaders[kind],
424
+ previewClient,
425
+ brandedFallbackSiteId
426
+ ),
427
+ labels: previewReferenceChrome[kind].labels
428
+ };
429
+ }
430
+ return createDynamicReferenceAdapters(
431
+ adapters,
432
+ (contentTypeKey) => createCachedDynamicContentReferenceAdapter(contentTypeKey, previewClient, brandedFallbackSiteId)
433
+ );
434
+ }
435
+ function PreviewFormProviderInner({ theme, children }) {
436
+ const { onPickImage, picker } = useRichTextImagePicker();
437
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
438
+ /* @__PURE__ */ jsx(RichTextWidgetProvider, { theme, onPickImage, children }),
439
+ picker
440
+ ] });
441
+ }
442
+ function PreviewFormProvider({
443
+ siteId: rawSiteId,
444
+ theme = null,
445
+ previewClient,
446
+ bearerToken,
447
+ supabaseUrl,
448
+ mediaAssets,
449
+ sdkConfig,
450
+ children
451
+ }) {
452
+ const siteId = useMemo(() => expectParsedBrandedId(parseSiteId(rawSiteId)), [rawSiteId]);
453
+ const linkAdapters = useMemo(() => createPreviewLinkAdapters(previewClient), [previewClient]);
454
+ const entryPickerAdapter = useMemo(() => createPreviewEntryPickerAdapter(previewClient), [previewClient]);
455
+ const siteOptionAdapters = useMemo(() => createPreviewSiteOptionAdapters(previewClient), [previewClient]);
456
+ const referenceAdapters = useMemo(
457
+ () => createPreviewReferenceAdapters(previewClient),
458
+ [previewClient]
459
+ );
460
+ const paletteTokens = useMemo(() => {
461
+ if (!theme) return void 0;
462
+ try {
463
+ const runtime = buildThemeRuntime(theme);
464
+ return runtime.paletteTokens;
465
+ } catch {
466
+ return void 0;
467
+ }
468
+ }, [theme]);
469
+ return /* @__PURE__ */ jsx(SdkConfigProvider, { sdkConfig: sdkConfig ?? null, children: /* @__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(SiteOptionWidgetProvider, { siteId, adapters: siteOptionAdapters, children: /* @__PURE__ */ jsx(
470
+ MediaEditorProvider,
471
+ {
472
+ siteId,
473
+ apiClient: previewClient,
474
+ bearerToken,
475
+ supabaseUrl,
476
+ mediaAssets,
477
+ children: /* @__PURE__ */ jsx(ButtonWidgetProvider, { buttonSystem: theme?.buttons ?? null, children: /* @__PURE__ */ jsx(PreviewFormProviderInner, { theme, children }) })
478
+ }
479
+ ) }) }) }) }) }) });
480
+ }
481
+ var previewWidgetOverrides = {
482
+ link: LinkField,
483
+ file: FileField,
484
+ media: MediaField,
485
+ richText: RichTextField,
486
+ reference: ReferenceField,
487
+ backgroundColor: BackgroundColorField,
488
+ backgroundGradient: BackgroundGradientField,
489
+ entryPicker: EntryPickerField,
490
+ iconPicker: IconPickerField,
491
+ buttonVariant: ButtonVariantField,
492
+ siteOptionPicker: SiteOptionPickerField
493
+ };
494
+ function PreviewZoomControls({ zoom, onZoomChange, disabled }) {
495
+ return /* @__PURE__ */ jsxs(
496
+ Select,
497
+ {
498
+ value: String(zoom),
499
+ onValueChange: (value) => onZoomChange(value === "fit" ? "fit" : Number(value)),
500
+ disabled,
501
+ children: [
502
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-20", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
503
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
504
+ /* @__PURE__ */ jsx(SelectItem, { value: "33", children: "33%" }),
505
+ /* @__PURE__ */ jsx(SelectItem, { value: "50", children: "50%" }),
506
+ /* @__PURE__ */ jsx(SelectItem, { value: "75", children: "75%" }),
507
+ /* @__PURE__ */ jsx(SelectItem, { value: "100", children: "100%" }),
508
+ /* @__PURE__ */ jsx(SelectItem, { value: "fit", children: "Fit" })
509
+ ] })
510
+ ]
511
+ }
512
+ );
513
+ }
514
+ var VIEWPORT_PRESETS = [
515
+ { label: "Mobile", width: 375, icon: Smartphone },
516
+ { label: "Tablet", width: 768, icon: Tablet },
517
+ { label: "Desktop", width: 1440, icon: Monitor },
518
+ { label: "Full Width", width: "full", icon: Maximize2 }
519
+ ];
520
+ function PreviewViewportControls({
521
+ mode,
522
+ width,
523
+ onModeChange,
524
+ onWidthChange,
525
+ className,
526
+ zoom,
527
+ onZoomChange
528
+ }) {
529
+ const activePreset = VIEWPORT_PRESETS.find((p) => p.width === width);
530
+ const ActiveIcon = activePreset?.icon ?? Monitor;
531
+ const activeLabel = mode === "squeeze" ? "Auto fit" : activePreset?.label ?? `${width}px`;
532
+ const showZoomControls = mode === "fixed" && zoom !== void 0 && onZoomChange !== void 0;
533
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), children: [
534
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
535
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "gap-2", children: [
536
+ /* @__PURE__ */ jsx(ActiveIcon, { className: "h-4 w-4" }),
537
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: activeLabel })
538
+ ] }) }),
539
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
540
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Preview size" }),
541
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
542
+ /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => onModeChange("squeeze"), className: cn(mode === "squeeze" && "bg-accent"), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
543
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Auto fit" }),
544
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Matches the space beside the editor" })
545
+ ] }) }),
546
+ /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => onModeChange("fixed"), className: cn(mode === "fixed" && "bg-accent"), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
547
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Choose size" }),
548
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Preview mobile, tablet, or desktop widths" })
549
+ ] }) })
550
+ ] })
551
+ ] }),
552
+ mode === "fixed" && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
553
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: activePreset?.label ?? `${width}px` }) }),
554
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
555
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Screen size" }),
556
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
557
+ VIEWPORT_PRESETS.map((preset) => /* @__PURE__ */ jsxs(
558
+ DropdownMenuItem,
559
+ {
560
+ onClick: () => onWidthChange(preset.width),
561
+ className: cn(width === preset.width && "bg-accent"),
562
+ children: [
563
+ /* @__PURE__ */ jsx(preset.icon, { className: "mr-2 h-4 w-4" }),
564
+ preset.label,
565
+ typeof preset.width === "number" && /* @__PURE__ */ jsxs("span", { className: "ml-auto text-xs text-muted-foreground", children: [
566
+ preset.width,
567
+ "px"
568
+ ] })
569
+ ]
570
+ },
571
+ preset.label
572
+ ))
573
+ ] })
574
+ ] }),
575
+ showZoomControls ? /* @__PURE__ */ jsx(PreviewZoomControls, { zoom, onZoomChange }) : null
576
+ ] });
577
+ }
578
+ function PreviewSidebarSection({
579
+ label,
580
+ defaultOpen = false,
581
+ openRequestId = null,
582
+ children
583
+ }) {
584
+ const [open, setOpen] = useState(defaultOpen || openRequestId !== null);
585
+ useEffect(() => {
586
+ if (openRequestId === null) return;
587
+ setOpen(true);
588
+ }, [openRequestId]);
589
+ return /* @__PURE__ */ jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
590
+ /* @__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: [
591
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${open ? "rotate-90" : ""}` }),
592
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label })
593
+ ] }) }),
594
+ /* @__PURE__ */ jsx(CollapsibleContent, { children })
595
+ ] }) });
596
+ }
597
+ function SubrouteNotice({ model }) {
598
+ return /* @__PURE__ */ jsxs("div", { className: "border-b bg-amber-50 px-4 py-3 text-amber-950", children: [
599
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: model.title }),
600
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm", children: model.body }),
601
+ model.cta ? /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsx(
602
+ "a",
603
+ {
604
+ 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",
605
+ href: model.cta.href,
606
+ target: "_blank",
607
+ rel: "noopener noreferrer",
608
+ children: model.cta.label
609
+ }
610
+ ) }) : null,
611
+ model.fallbackMessage ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-amber-900/90", children: model.fallbackMessage }) : null
612
+ ] });
613
+ }
614
+
615
+ // ../preview-next/src/client/preview/subrouteNotice.ts
616
+ function buildNoticeFromMeta(meta, payload, siteId) {
617
+ const name = meta.formatInstanceName(payload);
618
+ const title = name ? `You're previewing the ${name} ${meta.instanceNoun} of this ${meta.containerNoun}` : `You're previewing one ${meta.instanceNoun} of this ${meta.containerNoun}`;
619
+ 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}.`;
620
+ const ctaLabel = `Edit just this ${meta.instanceNoun}`;
621
+ let href = null;
622
+ try {
623
+ href = getDashboardUrl(`sites/${siteId}/${meta.subEditorPath(payload)}`);
624
+ } catch {
625
+ href = null;
626
+ }
627
+ return {
628
+ title,
629
+ body,
630
+ cta: href ? { label: ctaLabel, href } : null,
631
+ fallbackMessage: href ? null : "Dashboard links are unavailable. Set `NEXT_PUBLIC_DASHBOARD_URL` so preview can link back to the dashboard."
632
+ };
633
+ }
634
+ function resolveSubrouteNotice(ctx, args) {
635
+ if (!ctx) return null;
636
+ return foldSubroute(ctx, {
637
+ "event-occurrence": (occurrence) => buildNoticeFromMeta(SUBROUTE_KINDS["event-occurrence"], occurrence, args.siteId),
638
+ "course-run": (run) => buildNoticeFromMeta(SUBROUTE_KINDS["course-run"], run, args.siteId)
639
+ });
640
+ }
641
+ function EntryEditorPanel({
642
+ siteId,
643
+ entryData,
644
+ contentTypeSchema,
645
+ referenceContentTypeKeys,
646
+ draftContent,
647
+ currentTemplate,
648
+ theme,
649
+ apiClient,
650
+ bearerToken,
651
+ supabaseUrl,
652
+ entrySubrouteContext,
653
+ metadataPanel,
654
+ linkedEntrySection,
655
+ mediaAssets,
656
+ widgets,
657
+ viewportMode,
658
+ viewportWidth,
659
+ zoom,
660
+ onViewportModeChange,
661
+ onViewportWidthChange,
662
+ onZoomChange
663
+ }) {
664
+ const [contentOpen, setContentOpen] = useState(true);
665
+ const [templateOpen, setTemplateOpen] = useState(false);
666
+ const [detailsOpen, setDetailsOpen] = useState(false);
667
+ const contentFormFields = useMemo(
668
+ // content-model keeps a source-local structural field type so it can typecheck natively
669
+ // without importing @riverbankcms/blocks source. Preview is the integration boundary where
670
+ // those normalized fields are consumed as a block-form manifest.
671
+ () => contentTypeSchema ? mapContentSchemaToFormFields(contentTypeSchema, {
672
+ referenceContentTypeKeys
673
+ }) : [],
674
+ [contentTypeSchema, referenceContentTypeKeys]
675
+ );
676
+ const contentFormSpec = useMemo(
677
+ () => ({
678
+ name: "content-fields",
679
+ version: "1.0",
680
+ title: "Content",
681
+ component: "content-form",
682
+ description: "",
683
+ fields: contentFormFields,
684
+ slots: []
685
+ }),
686
+ [contentFormFields]
687
+ );
688
+ const contentInitialValues = useMemo(() => draftContent ?? {}, [draftContent]);
689
+ const subrouteNotice = useMemo(
690
+ () => resolveSubrouteNotice(entrySubrouteContext ?? null, { siteId }),
691
+ [entrySubrouteContext, siteId]
692
+ );
693
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", children: [
694
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-2", children: /* @__PURE__ */ jsx(
695
+ PreviewViewportControls,
696
+ {
697
+ mode: viewportMode,
698
+ width: viewportWidth,
699
+ onModeChange: onViewportModeChange,
700
+ onWidthChange: onViewportWidthChange,
701
+ zoom,
702
+ onZoomChange
703
+ }
704
+ ) }),
705
+ subrouteNotice ? /* @__PURE__ */ jsx(SubrouteNotice, { model: subrouteNotice }) : null,
706
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-0 overflow-auto", children: [
707
+ linkedEntrySection ? /* @__PURE__ */ jsx(
708
+ PreviewSidebarSection,
709
+ {
710
+ label: linkedEntrySection.label,
711
+ openRequestId: linkedEntrySection.openRequestId,
712
+ children: linkedEntrySection.content
713
+ }
714
+ ) : null,
715
+ contentTypeSchema && entryData.type ? /* @__PURE__ */ jsx(Collapsible, { open: contentOpen, onOpenChange: setContentOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
716
+ /* @__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: [
717
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${contentOpen ? "rotate-90" : ""}` }),
718
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Content" })
719
+ ] }) }),
720
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
721
+ PreviewFormProvider,
722
+ {
723
+ siteId,
724
+ theme: theme ?? null,
725
+ previewClient: apiClient,
726
+ bearerToken,
727
+ supabaseUrl,
728
+ mediaAssets,
729
+ children: /* @__PURE__ */ jsx(
730
+ ContentFieldsForm,
731
+ {
732
+ siteId,
733
+ form: contentFormSpec,
734
+ initialValues: contentInitialValues,
735
+ widgets
736
+ }
737
+ )
738
+ }
739
+ ) }) })
740
+ ] }) }) : /* @__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." }) }),
741
+ currentTemplate && entryData.type ? /* @__PURE__ */ jsx(Collapsible, { open: templateOpen, onOpenChange: setTemplateOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
742
+ /* @__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: [
743
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${templateOpen ? "rotate-90" : ""}` }),
744
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Template" })
745
+ ] }) }),
746
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-4", children: [
747
+ /* @__PURE__ */ jsx("div", { className: "rounded-md border px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
748
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: currentTemplate.name }),
749
+ currentTemplate.description ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: currentTemplate.description }) : null
750
+ ] }) }),
751
+ /* @__PURE__ */ jsx(
752
+ "a",
753
+ {
754
+ href: (() => {
755
+ try {
756
+ return getDashboardUrl(`sites/${siteId}/content/${entryData.type}/template`);
757
+ } catch {
758
+ return "#";
759
+ }
760
+ })(),
761
+ target: "_blank",
762
+ rel: "noopener noreferrer",
763
+ className: "inline-flex items-center text-sm text-primary hover:underline",
764
+ children: "Edit template in dashboard \u2192"
765
+ }
766
+ )
767
+ ] }) })
768
+ ] }) }) : null,
769
+ metadataPanel ? /* @__PURE__ */ jsx(Collapsible, { open: detailsOpen, onOpenChange: setDetailsOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
770
+ /* @__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: [
771
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${detailsOpen ? "rotate-90" : ""}` }),
772
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Details" })
773
+ ] }) }),
774
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: metadataPanel }) })
775
+ ] }) }) : null
776
+ ] })
777
+ ] });
778
+ }
779
+ var EMPTY_BLOCK_VALUES = {};
780
+ function SelectedBlockCustomizePanel({
781
+ blockItems,
782
+ uiStoreForBlockEditor,
783
+ theme,
784
+ sdkConfig
785
+ }) {
786
+ const customizeSelection = usePreviewBlockCustomizeSelectionOptional();
787
+ const selection = customizeSelection?.selection ?? { kind: "none" };
788
+ if (selection.kind === "none") {
789
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed px-3 py-4 text-sm text-muted-foreground", children: "Select a section in the preview to customize it." });
790
+ }
791
+ const selectedBlock = blockItems.find((block) => block.id === selection.blockId) ?? null;
792
+ if (!selectedBlock) {
793
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-md border border-dashed px-3 py-4 text-sm text-muted-foreground", children: [
794
+ /* @__PURE__ */ jsx("p", { children: "This section is no longer on the page." }),
795
+ /* @__PURE__ */ jsx(
796
+ "button",
797
+ {
798
+ type: "button",
799
+ className: "text-xs font-medium text-foreground underline underline-offset-4",
800
+ onClick: customizeSelection?.clearInspection,
801
+ children: "Clear selection"
802
+ }
803
+ )
804
+ ] });
805
+ }
806
+ return /* @__PURE__ */ jsx(
807
+ SelectedBlockCustomizeFields,
808
+ {
809
+ block: selectedBlock,
810
+ blockId: selection.blockId,
811
+ uiStoreForBlockEditor,
812
+ theme,
813
+ sdkConfig
814
+ }
815
+ );
816
+ }
817
+ function SelectedBlockCustomizeFields({
818
+ block,
819
+ blockId,
820
+ uiStoreForBlockEditor,
821
+ theme,
822
+ sdkConfig
823
+ }) {
824
+ const blockContent = useBlockCurrentValues(
825
+ uiStoreForBlockEditor,
826
+ blockId,
827
+ block.content ?? EMPTY_BLOCK_VALUES
828
+ );
829
+ const customizeControls = useBlockCustomizeControls({
830
+ blockId,
831
+ blockKind: block.kind,
832
+ content: blockContent,
833
+ uiStore: uiStoreForBlockEditor,
834
+ theme,
835
+ sdkConfig,
836
+ maxQuickFields: MAX_QUICK_MANIFEST_FIELDS
837
+ });
838
+ const { backend, manifest, panelFields, quickFields } = customizeControls;
839
+ if (!manifest || !backend) {
840
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed px-3 py-4 text-sm text-muted-foreground", children: "No customize controls are available for this section." });
841
+ }
842
+ if (quickFields.length === 0 && panelFields.length === 0) {
843
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1 rounded-md border border-dashed px-3 py-4 text-sm text-muted-foreground", children: [
844
+ /* @__PURE__ */ jsx("p", { children: "No safe customize controls are available for this section yet." }),
845
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: "Content fields remain in Page Blocks." })
846
+ ] });
847
+ }
848
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
849
+ /* @__PURE__ */ jsxs("div", { children: [
850
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", children: manifest.title }),
851
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: formatBlockKindLabel(block.kind) })
852
+ ] }),
853
+ quickFields.length > 0 ? /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
854
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: "Quick controls" }),
855
+ /* @__PURE__ */ jsx(
856
+ FormRenderer,
857
+ {
858
+ fields: quickFields,
859
+ backend,
860
+ widgetOverrides: previewWidgetOverrides,
861
+ showSettingsTrigger: false,
862
+ blockKind: block.kind,
863
+ className: "space-y-4"
864
+ }
865
+ )
866
+ ] }) : null,
867
+ panelFields.length > 0 ? /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
868
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: "More controls" }),
869
+ /* @__PURE__ */ jsx(
870
+ FormRenderer,
871
+ {
872
+ fields: panelFields,
873
+ backend,
874
+ widgetOverrides: previewWidgetOverrides,
875
+ showSettingsTrigger: false,
876
+ blockKind: block.kind,
877
+ className: "space-y-4"
878
+ }
879
+ )
880
+ ] }) : null
881
+ ] });
882
+ }
883
+ function useBlockCurrentValues(store, blockId, fallback) {
884
+ return useSyncExternalStore(
885
+ store.subscribe,
886
+ () => readBlockCurrentValues(store, blockId, fallback),
887
+ () => fallback
888
+ );
889
+ }
890
+ function readBlockCurrentValues(store, blockId, fallback) {
891
+ const values = store.getState().drafts.getCurrentValues(blockId);
892
+ return values ?? fallback;
893
+ }
894
+ function formatBlockKindLabel(blockKind) {
895
+ return blockKind.replace(/^block\./, "").replace(/^custom\./, "").replace(/[-_]+/g, " ");
896
+ }
897
+ function PageBlockEditorPanel({
898
+ siteId,
899
+ pageId,
900
+ theme,
901
+ apiClient,
902
+ bearerToken,
903
+ supabaseUrl,
904
+ blockItems,
905
+ uiStoreForBlockEditor,
906
+ blockEditorRef,
907
+ onBlocksChange,
908
+ metadataPanel,
909
+ linkedEntrySection,
910
+ designPanel,
911
+ sdkConfig,
912
+ mediaAssets,
913
+ viewportMode,
914
+ viewportWidth,
915
+ zoom,
916
+ onViewportModeChange,
917
+ onViewportWidthChange,
918
+ onZoomChange
919
+ }) {
920
+ const customizeSelection = usePreviewBlockCustomizeSelectionOptional();
921
+ const [designOpen, setDesignOpen] = useState(false);
922
+ const [customizeOpen, setCustomizeOpen] = useState(false);
923
+ const [contentOpen, setContentOpen] = useState(true);
924
+ const [detailsOpen, setDetailsOpen] = useState(false);
925
+ const selectionKind = customizeSelection?.selection.kind;
926
+ const selectionRequestId = customizeSelection?.selection.kind === "selected" ? customizeSelection.selection.requestId : 0;
927
+ const handleCustomizeBlock = useCallback(
928
+ (blockId) => {
929
+ customizeSelection?.inspectBlock(blockId);
930
+ setCustomizeOpen(true);
931
+ },
932
+ [customizeSelection]
933
+ );
934
+ const blockActionPresentation = useMemo(
935
+ () => customizeSelection ? { kind: "menu", customize: { kind: "external", onSelect: handleCustomizeBlock } } : { kind: "menu", customize: { kind: "modal" } },
936
+ [customizeSelection, handleCustomizeBlock]
937
+ );
938
+ useEffect(() => {
939
+ if (selectionKind === "selected") {
940
+ setCustomizeOpen(true);
941
+ }
942
+ }, [selectionKind, selectionRequestId]);
943
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", children: [
944
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-2", children: /* @__PURE__ */ jsx(
945
+ PreviewViewportControls,
946
+ {
947
+ mode: viewportMode,
948
+ width: viewportWidth,
949
+ onModeChange: onViewportModeChange,
950
+ onWidthChange: onViewportWidthChange,
951
+ zoom,
952
+ onZoomChange
953
+ }
954
+ ) }),
955
+ /* @__PURE__ */ jsx(
956
+ PreviewFormProvider,
957
+ {
958
+ siteId,
959
+ theme: theme ?? null,
960
+ previewClient: apiClient,
961
+ bearerToken,
962
+ supabaseUrl,
963
+ mediaAssets,
964
+ sdkConfig: sdkConfig ?? null,
965
+ children: /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-0 overflow-auto", children: [
966
+ linkedEntrySection ? /* @__PURE__ */ jsx(
967
+ PreviewSidebarSection,
968
+ {
969
+ label: linkedEntrySection.label,
970
+ openRequestId: linkedEntrySection.openRequestId,
971
+ children: linkedEntrySection.content
972
+ }
973
+ ) : null,
974
+ designPanel ? /* @__PURE__ */ jsx(Collapsible, { open: designOpen, onOpenChange: setDesignOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
975
+ /* @__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: [
976
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${designOpen ? "rotate-90" : ""}` }),
977
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Design" })
978
+ ] }) }),
979
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: designPanel }) })
980
+ ] }) }) : null,
981
+ /* @__PURE__ */ jsx(Collapsible, { open: customizeOpen, onOpenChange: setCustomizeOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
982
+ /* @__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: [
983
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${customizeOpen ? "rotate-90" : ""}` }),
984
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Customize" })
985
+ ] }) }),
986
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
987
+ SelectedBlockCustomizePanel,
988
+ {
989
+ blockItems,
990
+ uiStoreForBlockEditor,
991
+ theme: theme ?? null,
992
+ sdkConfig: sdkConfig ?? null
993
+ }
994
+ ) }) })
995
+ ] }) }),
996
+ /* @__PURE__ */ jsx(Collapsible, { open: contentOpen, onOpenChange: setContentOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
997
+ /* @__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: [
998
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${contentOpen ? "rotate-90" : ""}` }),
999
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Page Blocks" })
1000
+ ] }) }),
1001
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx(
1002
+ BlockEditor,
1003
+ {
1004
+ ref: blockEditorRef,
1005
+ siteId,
1006
+ pageId,
1007
+ blocks: blockItems,
1008
+ uiStore: uiStoreForBlockEditor,
1009
+ apiClient,
1010
+ onBlocksChange,
1011
+ actionPresentation: blockActionPresentation,
1012
+ widgetOverrides: previewWidgetOverrides,
1013
+ sdkConfig: sdkConfig ?? null,
1014
+ reorderControlMode: "drag-handle-only"
1015
+ },
1016
+ pageId
1017
+ ) }) })
1018
+ ] }) }),
1019
+ metadataPanel ? /* @__PURE__ */ jsx(Collapsible, { open: detailsOpen, onOpenChange: setDetailsOpen, children: /* @__PURE__ */ jsxs("div", { className: "border-b", children: [
1020
+ /* @__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: [
1021
+ /* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${detailsOpen ? "rotate-90" : ""}` }),
1022
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Page Details" })
1023
+ ] }) }),
1024
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: metadataPanel }) })
1025
+ ] }) }) : null
1026
+ ] })
1027
+ }
1028
+ )
1029
+ ] });
1030
+ }
1031
+ function PreviewEditorGuidanceCard({
1032
+ guidance,
1033
+ completeSetupDisabled,
1034
+ onCompleteSetup
1035
+ }) {
1036
+ return /* @__PURE__ */ jsxs(
1037
+ "div",
1038
+ {
1039
+ className: guidance.cardClassName,
1040
+ "data-preview-guidance-tone": guidance.tone,
1041
+ children: [
1042
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-tight text-slate-900", children: guidance.title }),
1043
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-5 text-slate-700", children: guidance.description }),
1044
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
1045
+ Button,
1046
+ {
1047
+ type: "button",
1048
+ size: "sm",
1049
+ variant: "outline",
1050
+ onClick: onCompleteSetup,
1051
+ disabled: completeSetupDisabled,
1052
+ children: guidance.ctaLabel
1053
+ }
1054
+ ) })
1055
+ ]
1056
+ }
1057
+ );
1058
+ }
1059
+
1060
+ // ../ui/src/palette/hex.ts
1061
+ function normalizeHex(input, fallback = "#000000") {
1062
+ if (!input) return fallback;
1063
+ const trimmed = input.trim().replace(/^#/, "").toLowerCase();
1064
+ const expanded = trimmed.length === 3 ? trimmed.split("").map((char) => char + char).join("") : trimmed;
1065
+ if (!/^[0-9a-f]{6}$/i.test(expanded)) {
1066
+ return fallback;
1067
+ }
1068
+ return `#${expanded}`;
1069
+ }
1070
+ function hexToRgb(hex) {
1071
+ const normalized = normalizeHex(hex).replace("#", "");
1072
+ const match = normalized.match(/.{2}/g);
1073
+ if (!match || match.length < 3) return null;
1074
+ const [r, g, b] = match.map((value) => parseInt(value, 16));
1075
+ return [r, g, b];
1076
+ }
1077
+ function luminanceChannel(raw) {
1078
+ const channel = raw / 255;
1079
+ return channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
1080
+ }
1081
+ function contrastRatio(hexA, hexB) {
1082
+ const rgbA = hexToRgb(hexA);
1083
+ const rgbB = hexToRgb(hexB);
1084
+ if (!rgbA || !rgbB) return 1;
1085
+ const lumA = 0.2126 * luminanceChannel(rgbA[0]) + 0.7152 * luminanceChannel(rgbA[1]) + 0.0722 * luminanceChannel(rgbA[2]);
1086
+ const lumB = 0.2126 * luminanceChannel(rgbB[0]) + 0.7152 * luminanceChannel(rgbB[1]) + 0.0722 * luminanceChannel(rgbB[2]);
1087
+ const brighter = Math.max(lumA, lumB);
1088
+ const darker = Math.min(lumA, lumB);
1089
+ return Number(((brighter + 0.05) / (darker + 0.05)).toFixed(2));
1090
+ }
1091
+ var DEFAULT_BRAND_ROLES = [
1092
+ { key: "primary", label: "Primary" },
1093
+ { key: "secondary", label: "Secondary" },
1094
+ { key: "accent", label: "Accent" }
1095
+ ];
1096
+ var DEFAULT_FEEDBACK_ROLES = [
1097
+ { key: "success", label: "Success" },
1098
+ { key: "warning", label: "Warning" },
1099
+ { key: "destructive", label: "Destructive" }
1100
+ ];
1101
+ var DEFAULT_SURFACE_ROLES = [
1102
+ { key: "background", label: "Background" },
1103
+ { key: "surface", label: "Surface" },
1104
+ { key: "text", label: "Text" },
1105
+ { key: "border", label: "Border" }
1106
+ ];
1107
+ function PaletteTokenEditor({
1108
+ palette,
1109
+ onChange,
1110
+ hasInvalidValues = false,
1111
+ brandRoles = DEFAULT_BRAND_ROLES,
1112
+ feedbackRoles = DEFAULT_FEEDBACK_ROLES,
1113
+ surfaceRoles = DEFAULT_SURFACE_ROLES,
1114
+ showContrastChecks = true,
1115
+ className
1116
+ }) {
1117
+ const paletteValues = useMemo(() => {
1118
+ const map = /* @__PURE__ */ new Map();
1119
+ for (const entry of palette) {
1120
+ if (entry?.name) map.set(entry.name, normalizeHex(entry.hex));
1121
+ }
1122
+ return map;
1123
+ }, [palette]);
1124
+ const getColor = (role) => paletteValues.get(role) ?? "#000000";
1125
+ const handleRoleChange = (role, hex) => {
1126
+ if (!isPlausibleHexInput(hex)) return;
1127
+ onChange(role, normalizeHex(hex));
1128
+ };
1129
+ const backgroundHex = getColor("background");
1130
+ const textHex = getColor("text");
1131
+ const primaryHex = getColor("primary");
1132
+ const contrast = contrastRatio(backgroundHex, textHex);
1133
+ const actionContrast = contrastRatio(primaryHex, backgroundHex);
1134
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
1135
+ 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,
1136
+ /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Brand & accents", roles: brandRoles, getColor, onChange: handleRoleChange }),
1137
+ /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Feedback", roles: feedbackRoles, getColor, onChange: handleRoleChange }),
1138
+ /* @__PURE__ */ jsx(ColorRoleGroup, { title: "Surfaces & text", roles: surfaceRoles, getColor, onChange: handleRoleChange }),
1139
+ showContrastChecks ? /* @__PURE__ */ jsxs(Fragment, { children: [
1140
+ /* @__PURE__ */ jsx(Separator, {}),
1141
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
1142
+ /* @__PURE__ */ jsx(
1143
+ ContrastCard,
1144
+ {
1145
+ title: "Background \u2194 Text contrast",
1146
+ value: contrast,
1147
+ threshold: 4.5,
1148
+ primaryLabel: "Background",
1149
+ primaryHex: backgroundHex,
1150
+ secondaryLabel: "Text",
1151
+ secondaryHex: textHex
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ jsx(
1155
+ ContrastCard,
1156
+ {
1157
+ title: "Primary action contrast",
1158
+ value: actionContrast,
1159
+ threshold: 3,
1160
+ primaryLabel: "Primary",
1161
+ primaryHex,
1162
+ secondaryLabel: "Background",
1163
+ secondaryHex: backgroundHex
1164
+ }
1165
+ )
1166
+ ] })
1167
+ ] }) : null
1168
+ ] });
1169
+ }
1170
+ function isPlausibleHexInput(value) {
1171
+ const trimmed = value.trim();
1172
+ return /^#?[0-9a-fA-F]{3}$/.test(trimmed) || /^#?[0-9a-fA-F]{6}$/.test(trimmed);
1173
+ }
1174
+ function ColorRoleGroup({ title, roles, getColor, onChange }) {
1175
+ if (roles.length === 0) return null;
1176
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1177
+ /* @__PURE__ */ jsxs("div", { children: [
1178
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-foreground", children: title }),
1179
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Adjust hex values or drag the color picker." })
1180
+ ] }),
1181
+ /* @__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)) })
1182
+ ] });
1183
+ }
1184
+ function ColorRoleField({ role, value, onChange }) {
1185
+ const inputId = useId();
1186
+ const colorPickerLabel = `${role.label} colour picker`;
1187
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted p-3 space-y-2", children: [
1188
+ /* @__PURE__ */ jsx(
1189
+ "label",
1190
+ {
1191
+ htmlFor: inputId,
1192
+ className: "text-xs uppercase text-muted-foreground font-medium leading-5 block",
1193
+ children: role.label
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsx(
1197
+ ColorPicker,
1198
+ {
1199
+ value,
1200
+ onChange: ((next) => {
1201
+ if (typeof next === "string") {
1202
+ onChange(role.key, next);
1203
+ }
1204
+ }),
1205
+ className: "w-full",
1206
+ "aria-label": colorPickerLabel
1207
+ }
1208
+ ),
1209
+ /* @__PURE__ */ jsx(
1210
+ Input,
1211
+ {
1212
+ id: inputId,
1213
+ value,
1214
+ onChange: (event) => onChange(role.key, event.target.value),
1215
+ className: "font-mono text-xs uppercase",
1216
+ placeholder: "#000000"
1217
+ }
1218
+ )
1219
+ ] });
1220
+ }
1221
+ function ContrastCard({ title, value, threshold, primaryLabel, primaryHex, secondaryLabel, secondaryHex }) {
1222
+ const passes = value >= threshold;
1223
+ return /* @__PURE__ */ jsxs(
1224
+ Surface,
1225
+ {
1226
+ variant: "background",
1227
+ radius: "lg",
1228
+ padding: "md",
1229
+ className: cn(
1230
+ "space-y-2 border text-sm",
1231
+ passes ? "border-success/40 bg-success/10 text-success" : "border-warning/40 bg-warning/10 text-warning-foreground"
1232
+ ),
1233
+ children: [
1234
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1235
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: title }),
1236
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold", children: [
1237
+ value,
1238
+ ":1"
1239
+ ] })
1240
+ ] }),
1241
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: passes ? "Contrast meets WCAG AA guidance." : `Increase contrast to reach ${threshold}:1 or higher.` }),
1242
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-2 text-xs", children: [
1243
+ /* @__PURE__ */ jsx(ContrastSwatch, { hex: primaryHex, label: primaryLabel }),
1244
+ /* @__PURE__ */ jsx(ContrastSwatch, { hex: secondaryHex, label: secondaryLabel })
1245
+ ] })
1246
+ ]
1247
+ }
1248
+ );
1249
+ }
1250
+ function ContrastSwatch({ hex, label }) {
1251
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1252
+ /* @__PURE__ */ jsx("span", { className: "h-6 w-6 rounded-full border", style: { backgroundColor: hex } }),
1253
+ /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs uppercase tracking-wide text-muted-foreground", children: [
1254
+ label,
1255
+ ": ",
1256
+ hex
1257
+ ] })
1258
+ ] });
1259
+ }
1260
+ var MAX_FONT_RESULTS_WHEN_SEARCHING = 12;
1261
+ var MAX_FONT_RESULTS_WHEN_BROWSING = 8;
1262
+ var PALETTE_BRAND_ROLES = [
1263
+ { key: "primary", label: "Primary" },
1264
+ { key: "primaryForeground", label: "Primary text" },
1265
+ { key: "secondary", label: "Secondary" },
1266
+ { key: "accent", label: "Accent" }
1267
+ ];
1268
+ var PALETTE_FEEDBACK_ROLES = [
1269
+ { key: "success", label: "Success" },
1270
+ { key: "warning", label: "Warning" },
1271
+ { key: "destructive", label: "Destructive" }
1272
+ ];
1273
+ var PALETTE_SURFACE_ROLES = [
1274
+ { key: "background", label: "Background" },
1275
+ { key: "surface", label: "Surface" },
1276
+ { key: "surfaceAlt", label: "Alternate surface" },
1277
+ { key: "muted", label: "Muted surface" },
1278
+ { key: "text", label: "Text" },
1279
+ { key: "border", label: "Border" }
1280
+ ];
1281
+ function StyleConfigurator({
1282
+ siteId,
1283
+ apiClient,
1284
+ theme,
1285
+ selectionId,
1286
+ selectionVersion = null,
1287
+ selectionUpdatedAt,
1288
+ initialSelections,
1289
+ headerSelectionContext,
1290
+ footerSelectionContext,
1291
+ expandedRowRequest = null,
1292
+ siteStyleAccess
1293
+ }) {
1294
+ const router = useRouter();
1295
+ const previewDesign = usePreviewDesignCoordinator();
1296
+ const siteChromeCustomize = useSiteChromeCustomizeOptional();
1297
+ const setSiteStyleStagedPreviewActive = siteChromeCustomize?.siteStyleHistory.setStagedPreviewActive;
1298
+ const {
1299
+ applyFailed,
1300
+ applyStarted,
1301
+ applySucceeded,
1302
+ clearPreview,
1303
+ previewTheme
1304
+ } = previewDesign;
1305
+ const [isRefreshPending, startRefreshTransition] = useTransition();
1306
+ const baseSnapshot = useMemo(
1307
+ () => resolveStyleConfiguratorInitialSnapshot({
1308
+ theme,
1309
+ initialSelections: {
1310
+ ...initialSelections,
1311
+ selectionVersion: initialSelections.selectionVersion ?? selectionVersion ?? 0,
1312
+ selectionUpdatedAt: initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
1313
+ },
1314
+ headerSelectionContext,
1315
+ footerSelectionContext
1316
+ }),
1317
+ [
1318
+ footerSelectionContext,
1319
+ headerSelectionContext,
1320
+ theme,
1321
+ initialSelections,
1322
+ selectionUpdatedAt,
1323
+ selectionVersion
1324
+ ]
1325
+ );
1326
+ const [state, setState] = useState(
1327
+ () => baseSnapshot ? createStyleConfiguratorState(baseSnapshot) : null
1328
+ );
1329
+ const hasLoadedState = state !== null;
1330
+ const hasStagedSiteStylePreview = state ? isPreviewingChange(state) : false;
1331
+ const stateRef = useRef(state);
1332
+ const clearPreviewRef = useRef(clearPreview);
1333
+ const runnerRef = useRef(createOptimisticActionRunner());
1334
+ const appliedExpandedRowRequestIdRef = useRef(null);
1335
+ useEffect(() => {
1336
+ stateRef.current = state;
1337
+ }, [state]);
1338
+ useEffect(() => {
1339
+ clearPreviewRef.current = clearPreview;
1340
+ }, [clearPreview]);
1341
+ useEffect(() => {
1342
+ if (!setSiteStyleStagedPreviewActive) return;
1343
+ setSiteStyleStagedPreviewActive(hasStagedSiteStylePreview);
1344
+ return () => {
1345
+ setSiteStyleStagedPreviewActive(false);
1346
+ };
1347
+ }, [hasStagedSiteStylePreview, setSiteStyleStagedPreviewActive]);
1348
+ useEffect(() => {
1349
+ if (!baseSnapshot) return;
1350
+ setState(
1351
+ (current) => current ? syncStyleConfiguratorState(current, baseSnapshot) : createStyleConfiguratorState(baseSnapshot)
1352
+ );
1353
+ }, [baseSnapshot]);
1354
+ useEffect(() => {
1355
+ if (!state || !baseSnapshot) {
1356
+ clearPreview("style-configurator");
1357
+ return;
1358
+ }
1359
+ if (!isPreviewingChange(state)) {
1360
+ clearPreview("style-configurator");
1361
+ return;
1362
+ }
1363
+ const siteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
1364
+ if (!siteStyle) {
1365
+ clearPreview("style-configurator");
1366
+ return;
1367
+ }
1368
+ const result = planSiteStylePreviewChange({
1369
+ siteStyle,
1370
+ selection: state.previewed,
1371
+ baseTheme: theme,
1372
+ headerSelectionContext,
1373
+ footerSelectionContext
1374
+ });
1375
+ previewTheme("style-configurator", result.themeOverlay, "site-style");
1376
+ }, [
1377
+ state,
1378
+ baseSnapshot,
1379
+ clearPreview,
1380
+ previewTheme,
1381
+ headerSelectionContext,
1382
+ footerSelectionContext,
1383
+ theme
1384
+ ]);
1385
+ useEffect(() => {
1386
+ const runner = runnerRef.current;
1387
+ return () => {
1388
+ runner.invalidate();
1389
+ clearPreviewRef.current("style-configurator");
1390
+ };
1391
+ }, []);
1392
+ useEffect(() => {
1393
+ if (!expandedRowRequest) return;
1394
+ if (appliedExpandedRowRequestIdRef.current === expandedRowRequest.id) return;
1395
+ setState((current) => {
1396
+ if (!current) return current;
1397
+ appliedExpandedRowRequestIdRef.current = expandedRowRequest.id;
1398
+ return openRow(
1399
+ current,
1400
+ expandedRowRequest.row === "header" ? "header-layout" : expandedRowRequest.row
1401
+ );
1402
+ });
1403
+ }, [expandedRowRequest, hasLoadedState]);
1404
+ const handlePreviewSiteStyle = useCallback(
1405
+ (siteStyleId) => {
1406
+ setState((current) => {
1407
+ if (!current) return current;
1408
+ const next = previewSiteStyle(current, siteStyleId);
1409
+ const siteStyle = getCuratedSiteStyle(siteStyleId);
1410
+ if (!siteStyle) return next;
1411
+ const result = planSiteStylePreviewChange({
1412
+ siteStyle,
1413
+ selection: next.previewed,
1414
+ baseTheme: theme,
1415
+ headerSelectionContext,
1416
+ footerSelectionContext
1417
+ });
1418
+ return applyResolvedSelections(next, {
1419
+ resolved: result.resolvedSelections,
1420
+ warnings: result.warnings
1421
+ });
1422
+ });
1423
+ },
1424
+ [footerSelectionContext, headerSelectionContext, theme]
1425
+ );
1426
+ const handlePreviewPersonality = useCallback((id) => {
1427
+ setState(
1428
+ (current) => current ? previewButtonPersonality(current, id) : current
1429
+ );
1430
+ }, []);
1431
+ const handlePreviewHeaderLayout = useCallback((id) => {
1432
+ if (!id) return;
1433
+ setState(
1434
+ (current) => current ? previewHeaderLayout(current, id) : current
1435
+ );
1436
+ }, []);
1437
+ const handlePreviewHeaderStyle = useCallback((id) => {
1438
+ if (!id) return;
1439
+ setState((current) => current ? previewHeaderStyle(current, id) : current);
1440
+ }, []);
1441
+ const handlePreviewFooterLook = useCallback((id) => {
1442
+ const plan = planSiteChromeLookSelection({ surface: "footer", lookId: id });
1443
+ if (plan.kind !== "footer") return;
1444
+ setState(
1445
+ (current) => current ? previewFooterLook(current, plan.next.footerLookId) : current
1446
+ );
1447
+ }, []);
1448
+ const handlePreviewPalette = useCallback((id) => {
1449
+ setState(
1450
+ (current) => current ? previewPaletteVariant(current, id) : current
1451
+ );
1452
+ }, []);
1453
+ const handlePreviewTypographyPreset = useCallback(
1454
+ (id) => {
1455
+ setState(
1456
+ (current) => current ? previewTypographyPreset(current, id) : current
1457
+ );
1458
+ },
1459
+ []
1460
+ );
1461
+ const handleBodyFontOverride = useCallback((fontFamily) => {
1462
+ setState(
1463
+ (current) => current ? setTypographyBodyFontOverride(current, fontFamily) : current
1464
+ );
1465
+ }, []);
1466
+ const handleHeadingFontOverride = useCallback((fontFamily) => {
1467
+ setState(
1468
+ (current) => current ? setTypographyHeadingFontOverride(current, fontFamily) : current
1469
+ );
1470
+ }, []);
1471
+ const handleClearTypographyOverrides = useCallback(() => {
1472
+ setState(
1473
+ (current) => current ? clearTypographyFontOverrides(current) : current
1474
+ );
1475
+ }, []);
1476
+ const handleTokenOverride = useCallback((role, hex) => {
1477
+ setState((current) => {
1478
+ if (!current) return current;
1479
+ const next = {
1480
+ ...current.previewed.paletteOverrides ?? {},
1481
+ [role]: hex
1482
+ };
1483
+ const parsed = paletteOverridesSchema.safeParse(next);
1484
+ if (!parsed.success) return current;
1485
+ return setPaletteOverrides(current, parsed.data);
1486
+ });
1487
+ }, []);
1488
+ const handleResetOverrides = useCallback(() => {
1489
+ setState(
1490
+ (current) => current ? setPaletteOverrides(current, null) : current
1491
+ );
1492
+ }, []);
1493
+ const handleToggleRow = useCallback((row) => {
1494
+ setState((current) => current ? toggleRow(current, row) : current);
1495
+ }, []);
1496
+ const handleCancel = useCallback(() => {
1497
+ setState((current) => current ? cancelPreview(current) : current);
1498
+ }, []);
1499
+ const handleDismissWarnings = useCallback(() => {
1500
+ setState((current) => current ? dismissWarnings(current) : current);
1501
+ }, []);
1502
+ const handleDismissNotices = useCallback(() => {
1503
+ setState((current) => current ? dismissNotices(current) : current);
1504
+ }, []);
1505
+ const handleApply = useCallback(async () => {
1506
+ const live = stateRef.current;
1507
+ if (!live || !isPreviewingChange(live)) return;
1508
+ if (!deriveStyleConfiguratorCapabilities(live).canApplyStaged) return;
1509
+ const previewed = live.previewed;
1510
+ const siteStyle = getCuratedSiteStyle(previewed.siteStyleId);
1511
+ if (!siteStyle) return;
1512
+ const compiled = planSiteStylePreviewChange({
1513
+ siteStyle,
1514
+ selection: previewed,
1515
+ baseTheme: theme,
1516
+ headerSelectionContext,
1517
+ footerSelectionContext
1518
+ });
1519
+ const body = buildApplySiteStyleSelectionBody({
1520
+ base: live.base,
1521
+ previewed: {
1522
+ ...previewed,
1523
+ buttonPersonalityId: compiled.resolvedSelections.buttonPersonalityId,
1524
+ paletteVariantId: compiled.resolvedSelections.paletteVariantId,
1525
+ headerLayoutId: compiled.resolvedSelections.headerLayoutId,
1526
+ headerStyleId: compiled.resolvedSelections.headerStyleId,
1527
+ headerLookId: compiled.resolvedSelections.headerLookId,
1528
+ typographyPresetId: compiled.resolvedSelections.typographyPresetId,
1529
+ typographyBodyFontOverride: compiled.resolvedSelections.typographyBodyFontOverride,
1530
+ typographyHeadingFontOverride: compiled.resolvedSelections.typographyHeadingFontOverride
1531
+ },
1532
+ selectionId
1533
+ });
1534
+ await runnerRef.current.run({
1535
+ optimistic: () => {
1536
+ applyStarted("style-configurator");
1537
+ setState((current) => current ? markApplySaving(current) : current);
1538
+ },
1539
+ network: async () => {
1540
+ const response = await apiClient({
1541
+ endpoint: "applySiteStyleSelection",
1542
+ params: { siteId },
1543
+ body
1544
+ });
1545
+ return unwrapResponse(response);
1546
+ },
1547
+ onSuccess: (saved) => {
1548
+ const persisted = selectionSnapshotFromApplySiteStyleSelectionResponse(
1549
+ saved,
1550
+ previewed
1551
+ );
1552
+ const historyEntry = styleConfiguratorHistoryEntryForPersistedSelection(
1553
+ live,
1554
+ persisted
1555
+ );
1556
+ setState(
1557
+ (current) => current ? markApplySaved(current, persisted) : current
1558
+ );
1559
+ if (historyEntry) {
1560
+ siteChromeCustomize?.siteStyleHistory.recordAppliedSelection(
1561
+ historyEntry
1562
+ );
1563
+ }
1564
+ applySucceeded("style-configurator");
1565
+ startRefreshTransition(() => {
1566
+ router.refresh();
1567
+ });
1568
+ },
1569
+ onError: (error) => {
1570
+ applyFailed("style-configurator");
1571
+ const applyError = classifyStyleApplyError(error);
1572
+ if (applyError.kind === "stale") {
1573
+ startRefreshTransition(() => {
1574
+ router.refresh();
1575
+ });
1576
+ }
1577
+ const message = applyError.message;
1578
+ setState(
1579
+ (current) => current ? markApplyFailed(
1580
+ current,
1581
+ message,
1582
+ applyError.kind === "stale" ? "stale" : "retryable"
1583
+ ) : current
1584
+ );
1585
+ }
1586
+ });
1587
+ }, [
1588
+ apiClient,
1589
+ footerSelectionContext,
1590
+ headerSelectionContext,
1591
+ applyFailed,
1592
+ applyStarted,
1593
+ applySucceeded,
1594
+ router,
1595
+ selectionId,
1596
+ siteChromeCustomize?.siteStyleHistory,
1597
+ siteId,
1598
+ startRefreshTransition,
1599
+ theme
1600
+ ]);
1601
+ if (!state || !baseSnapshot) {
1602
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading design controls..." });
1603
+ }
1604
+ const applyAllowed = canApplySiteStyle(siteStyleAccess);
1605
+ const stagedCapabilities = deriveStyleConfiguratorCapabilities(state);
1606
+ const previewingChange = isPreviewingChange(state);
1607
+ const saving = stagedCapabilities.isApplyingStaged || isRefreshPending;
1608
+ const currentSiteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
1609
+ if (!currentSiteStyle) {
1610
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "This site uses a Site Style that cannot be configured here." });
1611
+ }
1612
+ const currentPersonality = findButtonPersonality(
1613
+ state.previewed.buttonPersonalityId
1614
+ );
1615
+ const currentPaletteVariant = findPaletteVariant(
1616
+ state.previewed.paletteVariantId
1617
+ );
1618
+ const currentTypographyPreset = state.previewed.typographyPresetId ? findTypographyPreset(state.previewed.typographyPresetId) : null;
1619
+ const chromeLookModels = buildSiteChromeLookModels({
1620
+ state,
1621
+ siteStyle: currentSiteStyle,
1622
+ headerSelectionContext,
1623
+ footerSelectionContext
1624
+ });
1625
+ const currentHeaderLayout = getHeaderLayout(state.previewed.headerLayoutId);
1626
+ const currentHeaderStyle = getHeaderStyle(state.previewed.headerStyleId);
1627
+ const currentFooterLook = state.previewed.footerLookId ? findFooterLook(state.previewed.footerLookId) : findFooterLook(chromeLookModels.footer.renderedLookId);
1628
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1629
+ state.warnings.length > 0 ? /* @__PURE__ */ jsx(
1630
+ WarningNotice,
1631
+ {
1632
+ warnings: state.warnings,
1633
+ onDismiss: handleDismissWarnings
1634
+ }
1635
+ ) : null,
1636
+ state.notices.length > 0 ? /* @__PURE__ */ jsx(
1637
+ NoticeBanner,
1638
+ {
1639
+ notices: state.notices,
1640
+ onDismiss: handleDismissNotices
1641
+ }
1642
+ ) : null,
1643
+ /* @__PURE__ */ jsx(
1644
+ SiteStyleRow,
1645
+ {
1646
+ expanded: state.expandedRow === "style",
1647
+ onToggle: () => handleToggleRow("style"),
1648
+ current: currentSiteStyle,
1649
+ onPreview: handlePreviewSiteStyle,
1650
+ disabled: saving
1651
+ }
1652
+ ),
1653
+ /* @__PURE__ */ jsx(
1654
+ SiteChromeLookRow,
1655
+ {
1656
+ label: "Header layout",
1657
+ ariaLabel: "Header layout",
1658
+ defaultDescription: "Choose the header structure.",
1659
+ expanded: state.expandedRow === "header-layout",
1660
+ onToggle: () => handleToggleRow("header-layout"),
1661
+ currentId: state.previewed.headerLayoutId,
1662
+ currentLabel: currentHeaderLayout?.label ?? state.previewed.headerLayoutId,
1663
+ scopeDescription: "Changes the header structure on every page.",
1664
+ options: chromeLookModels.headerLayout.options,
1665
+ diagnostics: [],
1666
+ onCycle: (direction) => {
1667
+ const next = cycleSiteChromeLookId(
1668
+ chromeLookModels.headerLayout.options,
1669
+ state.previewed.headerLayoutId,
1670
+ direction
1671
+ );
1672
+ if (next !== void 0) handlePreviewHeaderLayout(next);
1673
+ },
1674
+ onPreview: handlePreviewHeaderLayout,
1675
+ disabled: saving
1676
+ }
1677
+ ),
1678
+ /* @__PURE__ */ jsx(
1679
+ SiteChromeLookRow,
1680
+ {
1681
+ label: "Header look",
1682
+ ariaLabel: "Header look",
1683
+ defaultDescription: "Choose the header visual style.",
1684
+ expanded: state.expandedRow === "header-style",
1685
+ onToggle: () => handleToggleRow("header-style"),
1686
+ currentId: state.previewed.headerStyleId,
1687
+ currentLabel: currentHeaderStyle?.label ?? state.previewed.headerStyleId,
1688
+ scopeDescription: "Changes the header surface and link style.",
1689
+ options: chromeLookModels.headerStyle.options,
1690
+ diagnostics: [],
1691
+ onCycle: (direction) => {
1692
+ const next = cycleSiteChromeLookId(
1693
+ chromeLookModels.headerStyle.options,
1694
+ state.previewed.headerStyleId,
1695
+ direction
1696
+ );
1697
+ if (next !== void 0) handlePreviewHeaderStyle(next);
1698
+ },
1699
+ onPreview: handlePreviewHeaderStyle,
1700
+ disabled: saving
1701
+ }
1702
+ ),
1703
+ /* @__PURE__ */ jsx(
1704
+ SiteChromeLookRow,
1705
+ {
1706
+ label: "Footer",
1707
+ ariaLabel: "Footer",
1708
+ defaultDescription: "Use the footer chosen for this site style.",
1709
+ catalog: footerLookCatalog,
1710
+ expanded: state.expandedRow === "footer",
1711
+ onToggle: () => handleToggleRow("footer"),
1712
+ currentId: state.previewed.footerLookId,
1713
+ currentLabel: state.previewed.footerLookId ? currentFooterLook?.label ?? "Unknown footer Look" : `${currentFooterLook?.label ?? "Site style"} (default)`,
1714
+ scopeDescription: "Changes the footer on every page.",
1715
+ options: chromeLookModels.footer.options,
1716
+ diagnostics: chromeLookModels.footer.diagnostics,
1717
+ onCycle: (direction) => {
1718
+ const next = cycleSiteChromeLookId(
1719
+ chromeLookModels.footer.options,
1720
+ state.previewed.footerLookId,
1721
+ direction
1722
+ );
1723
+ if (next !== void 0) handlePreviewFooterLook(next);
1724
+ },
1725
+ onPreview: handlePreviewFooterLook,
1726
+ disabled: saving
1727
+ }
1728
+ ),
1729
+ /* @__PURE__ */ jsx(
1730
+ PersonalityRow,
1731
+ {
1732
+ expanded: state.expandedRow === "buttons",
1733
+ onToggle: () => handleToggleRow("buttons"),
1734
+ siteStyle: currentSiteStyle,
1735
+ currentId: state.previewed.buttonPersonalityId,
1736
+ currentLabel: currentPersonality?.name ?? state.previewed.buttonPersonalityId,
1737
+ onPreview: handlePreviewPersonality,
1738
+ disabled: saving
1739
+ }
1740
+ ),
1741
+ /* @__PURE__ */ jsx(
1742
+ TypographyRow,
1743
+ {
1744
+ expanded: state.expandedRow === "typography",
1745
+ onToggle: () => handleToggleRow("typography"),
1746
+ apiClient,
1747
+ siteStyle: currentSiteStyle,
1748
+ currentPresetId: state.previewed.typographyPresetId,
1749
+ currentLabel: currentTypographyPreset?.name ?? "Custom typography",
1750
+ bodyFontOverride: state.previewed.typographyBodyFontOverride,
1751
+ headingFontOverride: state.previewed.typographyHeadingFontOverride,
1752
+ onPreviewPreset: handlePreviewTypographyPreset,
1753
+ onBodyFontOverride: handleBodyFontOverride,
1754
+ onHeadingFontOverride: handleHeadingFontOverride,
1755
+ onClearOverrides: handleClearTypographyOverrides,
1756
+ disabled: saving
1757
+ }
1758
+ ),
1759
+ /* @__PURE__ */ jsx(
1760
+ PaletteRow,
1761
+ {
1762
+ expanded: state.expandedRow === "palette",
1763
+ onToggle: () => handleToggleRow("palette"),
1764
+ siteStyle: currentSiteStyle,
1765
+ currentId: state.previewed.paletteVariantId,
1766
+ currentLabel: currentPaletteVariant?.name ?? state.previewed.paletteVariantId,
1767
+ currentSwatch: currentPaletteVariant?.colors.find((c) => c.name === "primary")?.hex ?? null,
1768
+ onPreview: handlePreviewPalette,
1769
+ disabled: saving
1770
+ }
1771
+ ),
1772
+ currentPaletteVariant ? /* @__PURE__ */ jsx(
1773
+ ColoursRow,
1774
+ {
1775
+ expanded: state.expandedRow === "colours",
1776
+ onToggle: () => handleToggleRow("colours"),
1777
+ variant: currentPaletteVariant,
1778
+ overrides: state.previewed.paletteOverrides,
1779
+ onTokenChange: handleTokenOverride,
1780
+ onReset: handleResetOverrides,
1781
+ disabled: saving
1782
+ }
1783
+ ) : null,
1784
+ previewingChange ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pt-2", children: [
1785
+ /* @__PURE__ */ jsx(
1786
+ Button,
1787
+ {
1788
+ type: "button",
1789
+ size: "sm",
1790
+ variant: "outline",
1791
+ onClick: handleCancel,
1792
+ disabled: !stagedCapabilities.canCancelStaged || saving,
1793
+ children: "Cancel"
1794
+ }
1795
+ ),
1796
+ applyAllowed ? /* @__PURE__ */ jsx(
1797
+ Button,
1798
+ {
1799
+ type: "button",
1800
+ size: "sm",
1801
+ onClick: handleApply,
1802
+ disabled: !stagedCapabilities.canApplyStaged || saving,
1803
+ children: saving ? "Applying\u2026" : "Apply"
1804
+ }
1805
+ ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Preview only \u2014 ask an admin to apply." })
1806
+ ] }) : null,
1807
+ state.saveStatus.kind === "saved" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-success", children: "Saved." }) : null,
1808
+ state.saveStatus.kind === "failed" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: state.saveStatus.message }) : null
1809
+ ] });
1810
+ }
1811
+ function StyleRowFrame({
1812
+ label,
1813
+ currentLabel,
1814
+ swatch,
1815
+ expanded,
1816
+ onToggle,
1817
+ children
1818
+ }) {
1819
+ return /* @__PURE__ */ jsx(Collapsible, { open: expanded, onOpenChange: onToggle, children: /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-background", children: [
1820
+ /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1821
+ "button",
1822
+ {
1823
+ type: "button",
1824
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-accent/30",
1825
+ "aria-expanded": expanded,
1826
+ children: [
1827
+ /* @__PURE__ */ jsx(
1828
+ ChevronRight,
1829
+ {
1830
+ className: cn("h-4 w-4 transition", expanded && "rotate-90")
1831
+ }
1832
+ ),
1833
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1834
+ /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: label }),
1835
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: currentLabel })
1836
+ ] }),
1837
+ swatch ? /* @__PURE__ */ jsx(
1838
+ "span",
1839
+ {
1840
+ className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
1841
+ style: { backgroundColor: swatch },
1842
+ "aria-hidden": "true"
1843
+ }
1844
+ ) : null
1845
+ ]
1846
+ }
1847
+ ) }),
1848
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "border-t px-3 py-3", children }) })
1849
+ ] }) });
1850
+ }
1851
+ function SiteStyleRow({
1852
+ expanded,
1853
+ onToggle,
1854
+ current,
1855
+ onPreview,
1856
+ disabled
1857
+ }) {
1858
+ return /* @__PURE__ */ jsx(
1859
+ StyleRowFrame,
1860
+ {
1861
+ label: "Site style",
1862
+ currentLabel: current.name,
1863
+ expanded,
1864
+ onToggle,
1865
+ children: /* @__PURE__ */ jsx(
1866
+ "ul",
1867
+ {
1868
+ role: "radiogroup",
1869
+ "aria-label": "Site style",
1870
+ className: "list-none space-y-1 p-0",
1871
+ children: curatedSiteStyles.map((style) => /* @__PURE__ */ jsx(
1872
+ PickerOption,
1873
+ {
1874
+ selected: style.id === current.id,
1875
+ label: style.name,
1876
+ description: style.description,
1877
+ onSelect: () => onPreview(style.id),
1878
+ disabled
1879
+ },
1880
+ style.id
1881
+ ))
1882
+ }
1883
+ )
1884
+ }
1885
+ );
1886
+ }
1887
+ function PersonalityRow({
1888
+ expanded,
1889
+ onToggle,
1890
+ siteStyle,
1891
+ currentId,
1892
+ currentLabel,
1893
+ onPreview,
1894
+ disabled
1895
+ }) {
1896
+ const options = useMemo(
1897
+ () => requireEntriesForCuratedChoices(
1898
+ buttonPersonalities,
1899
+ siteStyle.buttonPersonalityChoices
1900
+ ),
1901
+ [siteStyle.buttonPersonalityChoices]
1902
+ );
1903
+ return /* @__PURE__ */ jsx(
1904
+ StyleRowFrame,
1905
+ {
1906
+ label: "Button style",
1907
+ currentLabel,
1908
+ expanded,
1909
+ onToggle,
1910
+ children: /* @__PURE__ */ jsx(
1911
+ "ul",
1912
+ {
1913
+ role: "radiogroup",
1914
+ "aria-label": "Button style",
1915
+ className: "list-none space-y-1 p-0",
1916
+ children: options.map((personality) => /* @__PURE__ */ jsx(
1917
+ PickerOption,
1918
+ {
1919
+ selected: personality.id === currentId,
1920
+ label: personality.name,
1921
+ description: personality.description,
1922
+ onSelect: () => onPreview(personality.id),
1923
+ disabled
1924
+ },
1925
+ personality.id
1926
+ ))
1927
+ }
1928
+ )
1929
+ }
1930
+ );
1931
+ }
1932
+ function TypographyRow({
1933
+ expanded,
1934
+ onToggle,
1935
+ apiClient,
1936
+ siteStyle,
1937
+ currentPresetId,
1938
+ currentLabel,
1939
+ bodyFontOverride,
1940
+ headingFontOverride,
1941
+ onPreviewPreset,
1942
+ onBodyFontOverride,
1943
+ onHeadingFontOverride,
1944
+ onClearOverrides,
1945
+ disabled
1946
+ }) {
1947
+ const options = useMemo(
1948
+ () => requireEntriesForCuratedChoices(
1949
+ typographyPresets,
1950
+ siteStyle.typographyPresetChoices
1951
+ ),
1952
+ [siteStyle.typographyPresetChoices]
1953
+ );
1954
+ const fontCatalog = useFontCatalog(apiClient, expanded);
1955
+ const selectedPreset = currentPresetId ? findTypographyPreset(currentPresetId) : null;
1956
+ return /* @__PURE__ */ jsxs(
1957
+ StyleRowFrame,
1958
+ {
1959
+ label: "Typography",
1960
+ currentLabel,
1961
+ expanded,
1962
+ onToggle,
1963
+ children: [
1964
+ 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,
1965
+ /* @__PURE__ */ jsx(
1966
+ "ul",
1967
+ {
1968
+ role: "radiogroup",
1969
+ "aria-label": "Typography preset",
1970
+ className: "space-y-1",
1971
+ children: options.map((preset) => /* @__PURE__ */ jsx(
1972
+ TypographyPresetOption,
1973
+ {
1974
+ preset,
1975
+ selected: preset.id === currentPresetId,
1976
+ onSelect: () => onPreviewPreset(preset.id),
1977
+ disabled
1978
+ },
1979
+ preset.id
1980
+ ))
1981
+ }
1982
+ ),
1983
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-3 border-t pt-3", children: [
1984
+ selectedPreset ? /* @__PURE__ */ jsxs(Fragment, { children: [
1985
+ /* @__PURE__ */ jsx(
1986
+ TypographyFontSlotPicker,
1987
+ {
1988
+ label: "Body font",
1989
+ slot: "body",
1990
+ preset: selectedPreset,
1991
+ catalog: fontCatalog,
1992
+ value: bodyFontOverride,
1993
+ onChange: onBodyFontOverride,
1994
+ disabled
1995
+ }
1996
+ ),
1997
+ /* @__PURE__ */ jsx(
1998
+ TypographyFontSlotPicker,
1999
+ {
2000
+ label: "Heading font",
2001
+ slot: "heading",
2002
+ preset: selectedPreset,
2003
+ catalog: fontCatalog,
2004
+ value: headingFontOverride,
2005
+ onChange: onHeadingFontOverride,
2006
+ disabled
2007
+ }
2008
+ )
2009
+ ] }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Choose a typography preset to use curated font overrides." }),
2010
+ /* @__PURE__ */ jsx(
2011
+ Button,
2012
+ {
2013
+ type: "button",
2014
+ size: "sm",
2015
+ variant: "outline",
2016
+ onClick: onClearOverrides,
2017
+ disabled: disabled || !bodyFontOverride && !headingFontOverride,
2018
+ children: "Use preset fonts"
2019
+ }
2020
+ )
2021
+ ] })
2022
+ ]
2023
+ }
2024
+ );
2025
+ }
2026
+ function TypographyPresetOption({
2027
+ preset,
2028
+ selected,
2029
+ onSelect,
2030
+ disabled
2031
+ }) {
2032
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2033
+ "button",
2034
+ {
2035
+ type: "button",
2036
+ role: "radio",
2037
+ "aria-checked": selected,
2038
+ onClick: onSelect,
2039
+ disabled,
2040
+ className: cn(
2041
+ "w-full rounded-md border px-3 py-2 text-left transition disabled:cursor-not-allowed disabled:opacity-50",
2042
+ selected ? "border-primary bg-primary/5" : "border-border hover:bg-muted/60"
2043
+ ),
2044
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
2045
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2046
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: preset.name }),
2047
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: preset.description })
2048
+ ] }),
2049
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
2050
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold uppercase tracking-wide", children: preset.defaultFonts.heading.split(",")[0] }),
2051
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: preset.defaultFonts.body.split(",")[0] })
2052
+ ] })
2053
+ ] })
2054
+ }
2055
+ ) });
2056
+ }
2057
+ function TypographyFontSlotPicker({
2058
+ label,
2059
+ slot,
2060
+ preset,
2061
+ catalog,
2062
+ value,
2063
+ onChange,
2064
+ disabled
2065
+ }) {
2066
+ const [query, setQuery] = useState("");
2067
+ const defaultFont = primaryFontFamily(preset.defaultFonts[slot]);
2068
+ const selectedFamily = value ?? defaultFont;
2069
+ const recordsByFamily = useMemo(
2070
+ () => indexFontsByFamily(catalog.fonts),
2071
+ [catalog.fonts]
2072
+ );
2073
+ const selectedFit = evaluateTypographyFontSlot({
2074
+ slot,
2075
+ policy: preset.slotPolicies[slot],
2076
+ capability: capabilityForFamily(selectedFamily, recordsByFamily)
2077
+ });
2078
+ const recommendedFamilies = preset.recommendedFonts[slot];
2079
+ const recommendedOptions = useMemo(
2080
+ () => uniqueFontFamilies(recommendedFamilies),
2081
+ [recommendedFamilies]
2082
+ );
2083
+ const browseOptions = useMemo(() => {
2084
+ const normalizedQuery = query.trim().toLowerCase();
2085
+ const source = normalizedQuery.length > 0 ? catalog.fonts.filter(
2086
+ (font) => font.family.toLowerCase().includes(normalizedQuery)
2087
+ ) : catalog.fonts;
2088
+ return [...source].sort(
2089
+ (a, b) => sortFontForPresetSlot(a, b, slot, preset, recordsByFamily)
2090
+ ).slice(
2091
+ 0,
2092
+ normalizedQuery.length > 0 ? MAX_FONT_RESULTS_WHEN_SEARCHING : MAX_FONT_RESULTS_WHEN_BROWSING
2093
+ );
2094
+ }, [catalog.fonts, preset, query, recordsByFamily, slot]);
2095
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-2", "aria-label": label, children: [
2096
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
2097
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2098
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }),
2099
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm", children: value ? selectedFamily : `${defaultFont} (preset)` })
2100
+ ] }),
2101
+ /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit: selectedFit })
2102
+ ] }),
2103
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-1", children: [
2104
+ /* @__PURE__ */ jsxs(
2105
+ "button",
2106
+ {
2107
+ type: "button",
2108
+ className: cn(
2109
+ "rounded-md border px-3 py-2 text-left text-sm transition disabled:cursor-not-allowed disabled:opacity-50",
2110
+ value === null ? "border-foreground bg-accent/40" : "border-transparent hover:border-foreground/40 hover:bg-accent/20"
2111
+ ),
2112
+ onClick: () => onChange(null),
2113
+ disabled,
2114
+ children: [
2115
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Use preset font" }),
2116
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: defaultFont })
2117
+ ]
2118
+ }
2119
+ ),
2120
+ recommendedOptions.map((family) => /* @__PURE__ */ jsx(
2121
+ FontFamilyOption,
2122
+ {
2123
+ family,
2124
+ selected: value === family,
2125
+ fit: evaluateTypographyFontSlot({
2126
+ slot,
2127
+ policy: preset.slotPolicies[slot],
2128
+ capability: capabilityForFamily(family, recordsByFamily)
2129
+ }),
2130
+ onSelect: () => onChange(family),
2131
+ disabled
2132
+ },
2133
+ family
2134
+ ))
2135
+ ] }),
2136
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border bg-muted/20 p-2", children: [
2137
+ /* @__PURE__ */ jsx(
2138
+ "input",
2139
+ {
2140
+ type: "search",
2141
+ value: query,
2142
+ placeholder: `Search ${slot} fonts`,
2143
+ onChange: (event) => setQuery(event.currentTarget.value),
2144
+ disabled: disabled || catalog.kind === "loading",
2145
+ 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"
2146
+ }
2147
+ ),
2148
+ 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,
2149
+ browseOptions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "max-h-56 space-y-1 overflow-y-auto pr-1", children: browseOptions.map((font) => /* @__PURE__ */ jsx(
2150
+ FontFamilyOption,
2151
+ {
2152
+ family: font.family,
2153
+ selected: value === font.family,
2154
+ fit: evaluateTypographyFontSlot({
2155
+ slot,
2156
+ policy: preset.slotPolicies[slot],
2157
+ capability: capabilityFromFontRecord(font)
2158
+ }),
2159
+ onSelect: () => onChange(font.family),
2160
+ disabled
2161
+ },
2162
+ font.family
2163
+ )) }) : catalog.kind === "loaded" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No matching Google Fonts." }) : null
2164
+ ] }),
2165
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1", children: [
2166
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Brand or uploaded font name" }),
2167
+ /* @__PURE__ */ jsx(
2168
+ "input",
2169
+ {
2170
+ type: "text",
2171
+ value: value ?? "",
2172
+ placeholder: "Enter any font family",
2173
+ onChange: (event) => onChange(normalizeFontFamilyOverride(event.currentTarget.value)),
2174
+ disabled,
2175
+ 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"
2176
+ }
2177
+ )
2178
+ ] })
2179
+ ] });
2180
+ }
2181
+ function FontFamilyOption({
2182
+ family,
2183
+ selected,
2184
+ fit,
2185
+ onSelect,
2186
+ disabled
2187
+ }) {
2188
+ return /* @__PURE__ */ jsxs(
2189
+ "button",
2190
+ {
2191
+ type: "button",
2192
+ className: cn(
2193
+ "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",
2194
+ selected ? "border-foreground bg-accent/40" : "border-transparent bg-background hover:border-foreground/40 hover:bg-accent/20"
2195
+ ),
2196
+ onClick: onSelect,
2197
+ disabled,
2198
+ children: [
2199
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: family }),
2200
+ /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit })
2201
+ ]
2202
+ }
2203
+ );
2204
+ }
2205
+ function TypographyFontFitBadge({
2206
+ fit
2207
+ }) {
2208
+ return /* @__PURE__ */ jsx(
2209
+ "span",
2210
+ {
2211
+ className: cn(
2212
+ "shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium",
2213
+ fit.kind === "recommended" && "bg-emerald-100 text-emerald-800",
2214
+ fit.kind === "compatible" && "bg-sky-100 text-sky-800",
2215
+ fit.kind === "limited" && "bg-amber-100 text-amber-900",
2216
+ fit.kind === "risky" && "bg-rose-100 text-rose-800",
2217
+ fit.kind === "unknown" && "bg-muted text-muted-foreground"
2218
+ ),
2219
+ title: fit.reason,
2220
+ children: typographyFontFitLabel(fit.kind)
2221
+ }
2222
+ );
2223
+ }
2224
+ function useFontCatalog(apiClient, enabled) {
2225
+ const [catalog, setCatalog] = useState({
2226
+ kind: "idle",
2227
+ fonts: []
2228
+ });
2229
+ const requestedClientRef = useRef(null);
2230
+ useEffect(() => {
2231
+ if (!enabled) return;
2232
+ if (requestedClientRef.current === apiClient) return;
2233
+ requestedClientRef.current = apiClient;
2234
+ let cancelled = false;
2235
+ setCatalog({ kind: "loading", fonts: [] });
2236
+ async function loadFonts() {
2237
+ try {
2238
+ const response = await apiClient({ endpoint: "listFonts" });
2239
+ const fonts = unwrapResponse(response);
2240
+ if (!cancelled) {
2241
+ setCatalog({
2242
+ kind: "loaded",
2243
+ fonts: Array.isArray(fonts) ? fonts : []
2244
+ });
2245
+ }
2246
+ } catch (error) {
2247
+ if (!cancelled) {
2248
+ setCatalog({
2249
+ kind: "failed",
2250
+ fonts: [],
2251
+ message: error instanceof Error ? error.message : "Unable to load the font catalog."
2252
+ });
2253
+ }
2254
+ }
2255
+ }
2256
+ void loadFonts();
2257
+ return () => {
2258
+ cancelled = true;
2259
+ };
2260
+ }, [apiClient, enabled]);
2261
+ return catalog;
2262
+ }
2263
+ function indexFontsByFamily(fonts) {
2264
+ return new Map(fonts.map((font) => [font.family.toLowerCase(), font]));
2265
+ }
2266
+ function capabilityForFamily(family, recordsByFamily) {
2267
+ const record = recordsByFamily.get(family.toLowerCase());
2268
+ if (record) return capabilityFromFontRecord(record);
2269
+ return {
2270
+ source: "unknown",
2271
+ family,
2272
+ category: "unknown",
2273
+ variants: []
2274
+ };
2275
+ }
2276
+ function capabilityFromFontRecord(font) {
2277
+ return {
2278
+ source: "google",
2279
+ family: font.family,
2280
+ category: normalizeGoogleFontCategory(font.category),
2281
+ variants: font.variants
2282
+ };
2283
+ }
2284
+ function normalizeGoogleFontCategory(category) {
2285
+ switch (category) {
2286
+ case "serif":
2287
+ case "sans-serif":
2288
+ case "display":
2289
+ case "handwriting":
2290
+ case "monospace":
2291
+ return category;
2292
+ default:
2293
+ return "unknown";
2294
+ }
2295
+ }
2296
+ function sortFontForPresetSlot(a, b, slot, preset, recordsByFamily) {
2297
+ const aFit = evaluateTypographyFontSlot({
2298
+ slot,
2299
+ policy: preset.slotPolicies[slot],
2300
+ capability: capabilityForFamily(a.family, recordsByFamily)
2301
+ });
2302
+ const bFit = evaluateTypographyFontSlot({
2303
+ slot,
2304
+ policy: preset.slotPolicies[slot],
2305
+ capability: capabilityForFamily(b.family, recordsByFamily)
2306
+ });
2307
+ const fitDelta = typographyFontFitRank(aFit.kind) - typographyFontFitRank(bFit.kind);
2308
+ if (fitDelta !== 0) return fitDelta;
2309
+ const popularityDelta = (a.popularity ?? Infinity) - (b.popularity ?? Infinity);
2310
+ if (popularityDelta !== 0) return popularityDelta;
2311
+ return a.family.localeCompare(b.family);
2312
+ }
2313
+ function typographyFontFitRank(kind) {
2314
+ switch (kind) {
2315
+ case "recommended":
2316
+ return 0;
2317
+ case "compatible":
2318
+ return 1;
2319
+ case "limited":
2320
+ return 2;
2321
+ case "risky":
2322
+ return 3;
2323
+ case "unknown":
2324
+ return 4;
2325
+ default: {
2326
+ const exhaustive = kind;
2327
+ return exhaustive;
2328
+ }
2329
+ }
2330
+ }
2331
+ function typographyFontFitLabel(kind) {
2332
+ switch (kind) {
2333
+ case "recommended":
2334
+ return "Recommended";
2335
+ case "compatible":
2336
+ return "Compatible";
2337
+ case "limited":
2338
+ return "Limited";
2339
+ case "risky":
2340
+ return "Risky";
2341
+ case "unknown":
2342
+ return "Unknown";
2343
+ default: {
2344
+ const exhaustive = kind;
2345
+ return exhaustive;
2346
+ }
2347
+ }
2348
+ }
2349
+ function primaryFontFamily(cssFamily) {
2350
+ return cssFamily.split(",")[0]?.trim().replace(/^["']|["']$/gu, "") || cssFamily;
2351
+ }
2352
+ function uniqueFontFamilies(families) {
2353
+ const seen = /* @__PURE__ */ new Set();
2354
+ const result = [];
2355
+ for (const family of families) {
2356
+ const normalized = primaryFontFamily(family);
2357
+ const key = normalized.toLowerCase();
2358
+ if (seen.has(key)) continue;
2359
+ seen.add(key);
2360
+ result.push(normalized);
2361
+ }
2362
+ return result;
2363
+ }
2364
+ function normalizeFontFamilyOverride(value) {
2365
+ const trimmed = value.trim();
2366
+ return trimmed.length > 0 ? trimmed : null;
2367
+ }
2368
+ function SiteChromeLookRow({
2369
+ label,
2370
+ ariaLabel,
2371
+ defaultDescription,
2372
+ catalog,
2373
+ options = [
2374
+ {
2375
+ id: null,
2376
+ label: "Site style default",
2377
+ description: defaultDescription,
2378
+ available: true
2379
+ },
2380
+ ...(catalog ?? []).map((look) => ({ ...look, available: true }))
2381
+ ],
2382
+ diagnostics,
2383
+ scopeDescription,
2384
+ expanded,
2385
+ onToggle,
2386
+ currentId,
2387
+ currentLabel,
2388
+ onCycle,
2389
+ onPreview,
2390
+ disabled
2391
+ }) {
2392
+ return /* @__PURE__ */ jsxs(
2393
+ StyleRowFrame,
2394
+ {
2395
+ label,
2396
+ currentLabel,
2397
+ expanded,
2398
+ onToggle,
2399
+ children: [
2400
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
2401
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: scopeDescription }),
2402
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center overflow-hidden rounded-full border bg-background", children: [
2403
+ /* @__PURE__ */ jsx(
2404
+ "button",
2405
+ {
2406
+ type: "button",
2407
+ onClick: () => onCycle("prev"),
2408
+ className: "grid h-8 w-8 place-items-center hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
2409
+ "aria-label": `Previous ${label.toLowerCase()}`,
2410
+ disabled: disabled || cycleableSiteChromeOptions(options).length <= 1,
2411
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 14 })
2412
+ }
2413
+ ),
2414
+ /* @__PURE__ */ jsx(
2415
+ "button",
2416
+ {
2417
+ type: "button",
2418
+ onClick: () => onCycle("next"),
2419
+ className: "grid h-8 w-8 place-items-center border-l hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
2420
+ "aria-label": `Next ${label.toLowerCase()}`,
2421
+ disabled: disabled || cycleableSiteChromeOptions(options).length <= 1,
2422
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 })
2423
+ }
2424
+ )
2425
+ ] })
2426
+ ] }),
2427
+ 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,
2428
+ /* @__PURE__ */ jsx(
2429
+ "ul",
2430
+ {
2431
+ role: "radiogroup",
2432
+ "aria-label": ariaLabel,
2433
+ className: "list-none space-y-1 p-0",
2434
+ children: options.map((look) => /* @__PURE__ */ jsx(
2435
+ PickerOption,
2436
+ {
2437
+ selected: look.id === currentId,
2438
+ label: look.label,
2439
+ description: look.description,
2440
+ onSelect: () => onPreview(look.id),
2441
+ disabled: disabled || !look.available
2442
+ },
2443
+ look.id ?? "site-style-default"
2444
+ ))
2445
+ }
2446
+ )
2447
+ ]
2448
+ }
2449
+ );
2450
+ }
2451
+ function PaletteRow({
2452
+ expanded,
2453
+ onToggle,
2454
+ siteStyle,
2455
+ currentId,
2456
+ currentLabel,
2457
+ currentSwatch,
2458
+ onPreview,
2459
+ disabled
2460
+ }) {
2461
+ const options = useMemo(() => {
2462
+ const recommended = requireEntriesForCuratedChoices(
2463
+ paletteVariants,
2464
+ siteStyle.paletteVariantChoices
2465
+ );
2466
+ if (recommended.some((variant) => variant.id === currentId)) {
2467
+ return recommended;
2468
+ }
2469
+ const current = findPaletteVariant(currentId);
2470
+ return current ? [current, ...recommended] : recommended;
2471
+ }, [currentId, siteStyle.paletteVariantChoices]);
2472
+ return /* @__PURE__ */ jsxs(
2473
+ StyleRowFrame,
2474
+ {
2475
+ label: "Palette",
2476
+ currentLabel,
2477
+ swatch: currentSwatch,
2478
+ expanded,
2479
+ onToggle,
2480
+ children: [
2481
+ /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: "These palette bases are recommended for the current Site Style. Choose a base here, then edit exact colours in Palette colours." }),
2482
+ /* @__PURE__ */ jsx(
2483
+ "ul",
2484
+ {
2485
+ role: "radiogroup",
2486
+ "aria-label": "Palette",
2487
+ className: "list-none space-y-1 p-0",
2488
+ children: options.map((variant) => {
2489
+ const primaryHex = variant.colors.find(
2490
+ (c) => c.name === "primary"
2491
+ )?.hex;
2492
+ return /* @__PURE__ */ jsx(
2493
+ PickerOption,
2494
+ {
2495
+ selected: variant.id === currentId,
2496
+ label: variant.name,
2497
+ description: variant.description,
2498
+ swatch: primaryHex,
2499
+ onSelect: () => onPreview(variant.id),
2500
+ disabled
2501
+ },
2502
+ variant.id
2503
+ );
2504
+ })
2505
+ }
2506
+ )
2507
+ ]
2508
+ }
2509
+ );
2510
+ }
2511
+ function ColoursRow({
2512
+ expanded,
2513
+ onToggle,
2514
+ variant,
2515
+ overrides,
2516
+ onTokenChange,
2517
+ onReset,
2518
+ disabled
2519
+ }) {
2520
+ const mergedPalette = useMemo(() => {
2521
+ return variant.colors.map((color) => ({
2522
+ name: color.name,
2523
+ hex: overrides?.[color.name] ?? color.hex
2524
+ }));
2525
+ }, [variant, overrides]);
2526
+ const overrideCount = overrides ? Object.keys(overrides).length : 0;
2527
+ const currentLabel = overrideCount > 0 ? `${overrideCount} colour${overrideCount === 1 ? "" : "s"} customised` : "Using selected palette";
2528
+ return /* @__PURE__ */ jsx(
2529
+ StyleRowFrame,
2530
+ {
2531
+ label: "Palette colours",
2532
+ currentLabel,
2533
+ expanded,
2534
+ onToggle,
2535
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
2536
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Customise the palette tokens used across the site, including backgrounds, surfaces, text and borders." }),
2537
+ /* @__PURE__ */ jsx(
2538
+ PaletteTokenEditor,
2539
+ {
2540
+ palette: mergedPalette,
2541
+ onChange: onTokenChange,
2542
+ brandRoles: PALETTE_BRAND_ROLES,
2543
+ feedbackRoles: PALETTE_FEEDBACK_ROLES,
2544
+ surfaceRoles: PALETTE_SURFACE_ROLES,
2545
+ showContrastChecks: false
2546
+ }
2547
+ ),
2548
+ overrideCount > 0 ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
2549
+ Button,
2550
+ {
2551
+ type: "button",
2552
+ variant: "outline",
2553
+ size: "sm",
2554
+ onClick: onReset,
2555
+ disabled,
2556
+ children: "Reset to variant"
2557
+ }
2558
+ ) }) : null
2559
+ ] })
2560
+ }
2561
+ );
2562
+ }
2563
+ function PickerOption({
2564
+ selected,
2565
+ label,
2566
+ description,
2567
+ swatch,
2568
+ onSelect,
2569
+ disabled
2570
+ }) {
2571
+ return /* @__PURE__ */ jsx("li", { className: "list-none", children: /* @__PURE__ */ jsxs(
2572
+ "button",
2573
+ {
2574
+ type: "button",
2575
+ role: "radio",
2576
+ "aria-checked": selected,
2577
+ onClick: onSelect,
2578
+ disabled,
2579
+ className: cn(
2580
+ "flex w-full items-center gap-3 rounded-md border px-3 py-2 text-left transition",
2581
+ selected ? "border-foreground bg-accent/40 shadow-sm" : "border-transparent bg-background hover:border-foreground/40 hover:bg-accent/20",
2582
+ disabled && "cursor-not-allowed opacity-60"
2583
+ ),
2584
+ children: [
2585
+ swatch ? /* @__PURE__ */ jsx(
2586
+ "span",
2587
+ {
2588
+ className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
2589
+ style: { backgroundColor: swatch },
2590
+ "aria-hidden": "true"
2591
+ }
2592
+ ) : null,
2593
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2594
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: label }),
2595
+ description ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: description }) : null
2596
+ ] })
2597
+ ]
2598
+ }
2599
+ ) });
2600
+ }
2601
+ function classifyStyleApplyError(error) {
2602
+ if (isRecord(error)) {
2603
+ const status = error.status;
2604
+ const code = error.code;
2605
+ if (status === 409 || code === "resource:conflict") {
2606
+ return {
2607
+ kind: "stale",
2608
+ message: "This design changed elsewhere. Refreshing the latest version now."
2609
+ };
2610
+ }
2611
+ }
2612
+ return {
2613
+ kind: "other",
2614
+ message: error instanceof Error ? error.message : "Unable to apply your selection."
2615
+ };
2616
+ }
2617
+ function isRecord(value) {
2618
+ return typeof value === "object" && value !== null;
2619
+ }
2620
+ function WarningNotice({ warnings, onDismiss }) {
2621
+ return /* @__PURE__ */ jsxs(
2622
+ "div",
2623
+ {
2624
+ role: "status",
2625
+ className: "rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning-foreground",
2626
+ children: [
2627
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: warnings.map((warning, index) => /* @__PURE__ */ jsx("li", { children: describeWarning(warning) }, `${warning.kind}-${index}`)) }),
2628
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onDismiss, className: "mt-2 underline", children: "Dismiss" })
2629
+ ]
2630
+ }
2631
+ );
2632
+ }
2633
+ function NoticeBanner({ notices, onDismiss }) {
2634
+ return /* @__PURE__ */ jsxs(
2635
+ "div",
2636
+ {
2637
+ role: "status",
2638
+ className: "rounded-md border border-muted-foreground/20 bg-muted/40 px-3 py-2 text-xs",
2639
+ children: [
2640
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: notices.map((notice, index) => /* @__PURE__ */ jsx("li", { children: describeNotice(notice) }, `${notice.kind}-${index}`)) }),
2641
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onDismiss, className: "mt-2 underline", children: "Dismiss" })
2642
+ ]
2643
+ }
2644
+ );
2645
+ }
2646
+ function describeNotice(notice) {
2647
+ switch (notice.kind) {
2648
+ case "palette-overrides-hidden":
2649
+ return "Custom colours are saved for the previous palette.";
2650
+ case "palette-overrides-restored":
2651
+ return "Custom colours restored for this palette.";
2652
+ }
2653
+ }
2654
+ function describeWarning(warning) {
2655
+ switch (warning.kind) {
2656
+ case "rejected-personality":
2657
+ return `Button style changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
2658
+ case "rejected-palette":
2659
+ return `Palette changed to ${warning.fellBackTo} because ${warning.requested} is not available.`;
2660
+ case "rejected-typography-preset":
2661
+ return `Typography changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
2662
+ default: {
2663
+ const exhaustive = warning;
2664
+ return exhaustive;
2665
+ }
2666
+ }
2667
+ }
2668
+ function PreviewEditorSidebar({
2669
+ surfaceId,
2670
+ siteId,
2671
+ dashboardBaseUrl = null,
2672
+ pageId,
2673
+ entryData,
2674
+ contentTypeSchema,
2675
+ referenceContentTypeKeys = [],
2676
+ draftContent,
2677
+ currentTemplate,
2678
+ theme,
2679
+ themeId = null,
2680
+ themeSelectionId = null,
2681
+ themeSelectionVersion = null,
2682
+ themeSelectionUpdatedAt = null,
2683
+ themeSelectionButtonPersonalityId = null,
2684
+ themeSelectionPaletteVariantId = null,
2685
+ themeSelectionPaletteOverrides = null,
2686
+ themeSelectionHeaderLayoutId = null,
2687
+ themeSelectionHeaderStyleId = null,
2688
+ themeSelectionTypographyPresetId = null,
2689
+ themeSelectionTypographyBodyFontOverride = null,
2690
+ themeSelectionTypographyHeadingFontOverride = null,
2691
+ themeSelectionHeaderLookId = null,
2692
+ themeSelectionFooterLookId = null,
2693
+ themeSelectionHeaderContext,
2694
+ themeSelectionFooterContext,
2695
+ designRowRequest = null,
2696
+ apiClient,
2697
+ bearerToken,
2698
+ supabaseUrl,
2699
+ entrySubrouteContext,
2700
+ assistantPanel,
2701
+ sdkConfig,
2702
+ mediaAssets,
2703
+ siteStyleAccess = { kind: "hidden" },
2704
+ activeTab,
2705
+ onTabChange,
2706
+ onClose,
2707
+ viewportMode,
2708
+ viewportWidth,
2709
+ zoom,
2710
+ onViewportModeChange,
2711
+ onViewportWidthChange,
2712
+ onZoomChange
2713
+ }) {
2714
+ const controller = useEntryController();
2715
+ const controllerBlocks = controller.blocks;
2716
+ const uiStore = useEditorUiStoreInstance();
2717
+ const uiStoreForBlockEditor = useMemo(
2718
+ () => ({
2719
+ getState: uiStore.getState,
2720
+ subscribe: (listener) => uiStore.subscribe(() => listener())
2721
+ }),
2722
+ [uiStore]
2723
+ );
2724
+ const focusRequest = useUISelector(uiStore, (s) => s.focusRequest);
2725
+ const activeLinkedEntrySnapshot = useUISelector(
2726
+ uiStore,
2727
+ (s) => s.linkedEntries.getSelectedSnapshot()
2728
+ );
2729
+ const previewInspection = useUISelector(uiStore, (s) => s.previewInspection);
2730
+ const { canEdit } = usePreviewAuth();
2731
+ const dashboardSetupHandoff = useMemo(
2732
+ () => resolvePreviewDashboardSetupHandoff(),
2733
+ []
2734
+ );
2735
+ const dashboardContinueUrl = useMemo(
2736
+ () => buildPreviewDashboardSetupUrl(dashboardBaseUrl, dashboardSetupHandoff),
2737
+ [dashboardBaseUrl, dashboardSetupHandoff]
2738
+ );
2739
+ const sidebarChrome = useMemo(
2740
+ () => resolvePreviewSidebarChromeModel(surfaceId),
2741
+ [surfaceId]
2742
+ );
2743
+ const editorGuidance = useMemo(
2744
+ () => resolvePreviewEditorGuidanceModel({
2745
+ surfaceId,
2746
+ canEdit,
2747
+ hasDashboardSetupHandoff: Boolean(dashboardSetupHandoff)
2748
+ }),
2749
+ [canEdit, dashboardSetupHandoff, surfaceId]
2750
+ );
2751
+ const showAssistantTab = previewAssistantEnabled && Boolean(assistantPanel);
2752
+ const assistantAvailable = showAssistantTab && Boolean(pageId && bearerToken && canEdit);
2753
+ const effectiveActiveTab = showAssistantTab ? resolvePreviewSidebarTab(activeTab) : "editor";
2754
+ const blockItems = useMemo(
2755
+ () => controllerBlocks.list,
2756
+ [controllerBlocks.list]
2757
+ );
2758
+ const blockEditorRef = useRef(null);
2759
+ const contentFormWidgets = useMemo(() => previewWidgetOverrides, []);
2760
+ const handleBlocksChange = useCallback(
2761
+ (nextItems) => {
2762
+ controllerBlocks.actions.setList(nextItems);
2763
+ const drafts = uiStore.getState().drafts;
2764
+ const extractData = (content) => {
2765
+ if (!content || typeof content !== "object") return null;
2766
+ const obj = content;
2767
+ if ("data" in obj && obj.data && typeof obj.data === "object") {
2768
+ return obj.data;
2769
+ }
2770
+ return obj;
2771
+ };
2772
+ for (const block of nextItems) {
2773
+ if (!block.id) continue;
2774
+ const baseline = extractData(
2775
+ block.content ?? block.draftContent ?? block.publishedContent ?? null
2776
+ );
2777
+ const blockId = unsafeAsEditorBlockId(block.id);
2778
+ if (drafts.getBlockSnapshot(blockId)) {
2779
+ drafts.updateBlockBaseline(blockId, baseline, { kind: block.kind });
2780
+ } else {
2781
+ drafts.initNewBlock(blockId, baseline, { kind: block.kind });
2782
+ }
2783
+ }
2784
+ },
2785
+ [controllerBlocks.actions, uiStore]
2786
+ );
2787
+ useEffect(() => {
2788
+ const req = focusRequest;
2789
+ if (!req?.blockId) return;
2790
+ blockEditorRef.current?.focusField({
2791
+ blockId: req.blockId,
2792
+ fieldPath: req.fieldPath ?? void 0,
2793
+ itemIndex: req.itemIndex ?? void 0
2794
+ });
2795
+ uiStore.getState().clearFocus();
2796
+ }, [focusRequest, uiStore]);
2797
+ const metadataSectionContent = useMemo(() => {
2798
+ if (!pageId && !entryData) return null;
2799
+ return /* @__PURE__ */ jsx(
2800
+ PreviewMetadataPanel,
2801
+ {
2802
+ ownerKind: entryData ? "content" : "page",
2803
+ contentTypeKey: entryData?.type ?? null,
2804
+ entry: controller.entry
2805
+ }
2806
+ );
2807
+ }, [controller.entry, entryData, pageId]);
2808
+ const linkedEntrySection = buildLinkedEntrySidebarSection({
2809
+ snapshot: activeLinkedEntrySnapshot,
2810
+ inspection: previewInspection,
2811
+ renderContent: (snapshot) => /* @__PURE__ */ jsx(
2812
+ LinkedEntryEditorPanel,
2813
+ {
2814
+ siteId,
2815
+ apiClient,
2816
+ bearerToken,
2817
+ supabaseUrl,
2818
+ theme,
2819
+ mediaAssets,
2820
+ widgets: contentFormWidgets,
2821
+ entry: snapshot,
2822
+ fields: findLinkedEntryFieldsFromSnapshot(snapshot)
2823
+ }
2824
+ )
2825
+ });
2826
+ const editorPanel = entryData ? /* @__PURE__ */ jsx(
2827
+ EntryEditorPanel,
2828
+ {
2829
+ siteId,
2830
+ entryData,
2831
+ contentTypeSchema,
2832
+ referenceContentTypeKeys,
2833
+ draftContent,
2834
+ currentTemplate,
2835
+ theme,
2836
+ apiClient,
2837
+ bearerToken,
2838
+ supabaseUrl,
2839
+ entrySubrouteContext,
2840
+ metadataPanel: metadataSectionContent,
2841
+ linkedEntrySection,
2842
+ mediaAssets,
2843
+ widgets: contentFormWidgets,
2844
+ viewportMode,
2845
+ viewportWidth,
2846
+ zoom,
2847
+ onViewportModeChange,
2848
+ onViewportWidthChange,
2849
+ onZoomChange
2850
+ }
2851
+ ) : pageId ? /* @__PURE__ */ jsx(
2852
+ PageBlockEditorPanel,
2853
+ {
2854
+ siteId,
2855
+ pageId,
2856
+ theme,
2857
+ apiClient,
2858
+ bearerToken,
2859
+ supabaseUrl,
2860
+ blockItems,
2861
+ uiStoreForBlockEditor,
2862
+ blockEditorRef,
2863
+ onBlocksChange: handleBlocksChange,
2864
+ metadataPanel: metadataSectionContent,
2865
+ designPanel: canInspectSiteStyle(siteStyleAccess) ? /* @__PURE__ */ jsx(
2866
+ StyleConfigurator,
2867
+ {
2868
+ siteId,
2869
+ apiClient,
2870
+ theme,
2871
+ themeId,
2872
+ selectionId: themeSelectionId,
2873
+ selectionVersion: themeSelectionVersion,
2874
+ selectionUpdatedAt: themeSelectionUpdatedAt,
2875
+ initialSelections: {
2876
+ selectionVersion: themeSelectionVersion,
2877
+ selectionUpdatedAt: themeSelectionUpdatedAt,
2878
+ buttonPersonalityId: themeSelectionButtonPersonalityId,
2879
+ paletteVariantId: themeSelectionPaletteVariantId,
2880
+ paletteOverrides: themeSelectionPaletteOverrides,
2881
+ headerLayoutId: themeSelectionHeaderLayoutId,
2882
+ headerStyleId: themeSelectionHeaderStyleId,
2883
+ typographyPresetId: themeSelectionTypographyPresetId,
2884
+ typographyBodyFontOverride: themeSelectionTypographyBodyFontOverride,
2885
+ typographyHeadingFontOverride: themeSelectionTypographyHeadingFontOverride,
2886
+ headerLookId: themeSelectionHeaderLookId,
2887
+ footerLookId: themeSelectionFooterLookId
2888
+ },
2889
+ headerSelectionContext: themeSelectionHeaderContext,
2890
+ footerSelectionContext: themeSelectionFooterContext,
2891
+ expandedRowRequest: designRowRequest,
2892
+ siteStyleAccess
2893
+ }
2894
+ ) : null,
2895
+ sdkConfig: sdkConfig ?? null,
2896
+ mediaAssets,
2897
+ linkedEntrySection,
2898
+ viewportMode,
2899
+ viewportWidth,
2900
+ zoom,
2901
+ onViewportModeChange,
2902
+ onViewportWidthChange,
2903
+ onZoomChange
2904
+ }
2905
+ ) : /* @__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." }) }) });
2906
+ 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." });
2907
+ return /* @__PURE__ */ jsx(
2908
+ "div",
2909
+ {
2910
+ 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}`,
2911
+ "data-preview-sidebar-tone": sidebarChrome.tone,
2912
+ children: /* @__PURE__ */ jsxs(
2913
+ Tabs,
2914
+ {
2915
+ value: effectiveActiveTab,
2916
+ onValueChange: (value) => onTabChange(
2917
+ resolvePreviewSidebarTab(value)
2918
+ ),
2919
+ className: "flex h-full min-h-0 flex-col overflow-hidden",
2920
+ children: [
2921
+ /* @__PURE__ */ jsxs(
2922
+ "div",
2923
+ {
2924
+ className: `shrink-0 flex items-center justify-between px-4 py-3 ${sidebarChrome.headerClassName}`,
2925
+ children: [
2926
+ /* @__PURE__ */ jsxs(TabsList, { className: "h-8", children: [
2927
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "editor", children: "Editor" }),
2928
+ showAssistantTab ? /* @__PURE__ */ jsx(TabsTrigger, { value: "assistant", children: "AI Assistant" }) : null
2929
+ ] }),
2930
+ /* @__PURE__ */ jsx(
2931
+ Button,
2932
+ {
2933
+ variant: "ghost",
2934
+ size: "icon",
2935
+ onClick: onClose,
2936
+ "aria-label": "Close sidebar",
2937
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
2938
+ }
2939
+ )
2940
+ ]
2941
+ }
2942
+ ),
2943
+ editorGuidance ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-4 py-3", children: /* @__PURE__ */ jsx(
2944
+ PreviewEditorGuidanceCard,
2945
+ {
2946
+ guidance: editorGuidance,
2947
+ completeSetupDisabled: !dashboardContinueUrl,
2948
+ onCompleteSetup: () => {
2949
+ if (!dashboardContinueUrl || typeof window === "undefined")
2950
+ return;
2951
+ window.location.href = dashboardContinueUrl;
2952
+ }
2953
+ }
2954
+ ) }) : null,
2955
+ /* @__PURE__ */ jsx(
2956
+ TabsContent,
2957
+ {
2958
+ value: "editor",
2959
+ forceMount: true,
2960
+ className: "mt-0 min-h-0 flex-1 overflow-hidden data-[state=inactive]:hidden",
2961
+ children: editorPanel
2962
+ }
2963
+ ),
2964
+ showAssistantTab ? /* @__PURE__ */ jsx(
2965
+ TabsContent,
2966
+ {
2967
+ value: "assistant",
2968
+ forceMount: true,
2969
+ className: "mt-0 min-h-0 flex-1 overflow-hidden data-[state=inactive]:hidden",
2970
+ children: assistantPanelDisplay
2971
+ }
2972
+ ) : null
2973
+ ]
2974
+ }
2975
+ )
2976
+ }
2977
+ );
2978
+ }
2979
+ function PreviewMetadataPanel({
2980
+ ownerKind,
2981
+ contentTypeKey,
2982
+ entry
2983
+ }) {
2984
+ const uiStore = useEditorUiStoreInstance();
2985
+ const currentSlug = useEditorUiStore(
2986
+ (state) => state.metadata.getAtPath("draftSlug")
2987
+ ) ?? void 0;
2988
+ const metadataPresentation = useMemo(() => {
2989
+ if (ownerKind === "content") {
2990
+ return deriveRouteMetadataPresentation(
2991
+ { kind: "content", typeKey: contentTypeKey },
2992
+ { omitTitle: true }
2993
+ );
2994
+ }
2995
+ const routeSlug = resolvePageRouteSlug(entry, currentSlug);
2996
+ if (!routeSlug) {
2997
+ throw new Error("Expected routable page entry for preview metadata");
2998
+ }
2999
+ return deriveRouteMetadataPresentation(
3000
+ { kind: "page", routeSlug },
3001
+ { omitTitle: true }
3002
+ );
3003
+ }, [contentTypeKey, currentSlug, entry, ownerKind]);
3004
+ const manifest = metadataPresentation.manifest;
3005
+ const isHomepage = metadataPresentation.kind === "page-homepage";
3006
+ const validation = useMemo(
3007
+ () => createManifestValidation(manifest),
3008
+ [manifest]
3009
+ );
3010
+ const backend = useMemo(
3011
+ () => createStoreBackend({
3012
+ scope: "metadata",
3013
+ store: uiStore,
3014
+ validateFn: (values) => {
3015
+ const result = validation.schema.safeParse(values);
3016
+ if (!result.success) {
3017
+ return result.error.issues;
3018
+ }
3019
+ return [];
3020
+ }
3021
+ }),
3022
+ [uiStore, validation]
3023
+ );
3024
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3025
+ isHomepage ? /* @__PURE__ */ jsx("div", { className: "inline-flex rounded-md bg-muted px-2 py-1 text-xs font-medium text-muted-foreground", children: "Homepage" }) : null,
3026
+ /* @__PURE__ */ jsx(
3027
+ ManifestForm,
3028
+ {
3029
+ manifest,
3030
+ backend,
3031
+ className: "space-y-4"
3032
+ }
3033
+ )
3034
+ ] });
3035
+ }
3036
+ function LinkedEntryEditorPanel({
3037
+ siteId,
3038
+ apiClient,
3039
+ bearerToken,
3040
+ supabaseUrl,
3041
+ theme,
3042
+ mediaAssets,
3043
+ widgets,
3044
+ entry,
3045
+ fields
3046
+ }) {
3047
+ const uiStore = useEditorUiStoreInstance();
3048
+ const linkedEntriesAutosave = useUISelector(
3049
+ uiStore,
3050
+ (state) => state.autosave.linkedEntries
3051
+ );
3052
+ const manifest = useMemo(
3053
+ () => ({
3054
+ name: `linked-content-${entry.meta.contentTypeKey}`,
3055
+ version: "1.0",
3056
+ title: entry.meta.contentTypeLabel,
3057
+ component: "content-form",
3058
+ description: "",
3059
+ fields: fields.map((field) => ({
3060
+ ...field,
3061
+ label: fieldIdToLabel(field.id)
3062
+ })),
3063
+ slots: []
3064
+ }),
3065
+ [entry.meta.contentTypeKey, entry.meta.contentTypeLabel, fields]
3066
+ );
3067
+ const validation = useMemo(
3068
+ () => createManifestValidation(manifest, { widgets }),
3069
+ [manifest, widgets]
3070
+ );
3071
+ const backend = useMemo(
3072
+ () => createStoreBackend({
3073
+ scope: "linkedContent",
3074
+ store: uiStore,
3075
+ validateFn: (values) => {
3076
+ const result = validation.schema.safeParse(values);
3077
+ return result.success ? [] : result.error.issues;
3078
+ }
3079
+ }),
3080
+ [uiStore, validation]
3081
+ );
3082
+ const syncState = entry.syncState;
3083
+ const errorMessage = syncState.kind === "failed" ? syncState.error : null;
3084
+ const dirty = syncState.kind === "pending" || syncState.kind === "failed";
3085
+ const isSaving = linkedEntriesAutosave.status === "saving" && dirty;
3086
+ const displayTitle = resolveContentTitle(
3087
+ entry.current ?? entry.baseline ?? null,
3088
+ entry.meta.titleField,
3089
+ entry.meta.title
3090
+ );
3091
+ return /* @__PURE__ */ jsxs("div", { className: "bg-muted/20", children: [
3092
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 px-4 py-3", children: [
3093
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: displayTitle }) }),
3094
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: /* @__PURE__ */ jsx(
3095
+ Button,
3096
+ {
3097
+ type: "button",
3098
+ variant: "outline",
3099
+ size: "sm",
3100
+ onClick: () => {
3101
+ if (dirty) {
3102
+ uiStore.getState().linkedEntries.discard(entry.meta.entryId);
3103
+ } else {
3104
+ uiStore.getState().linkedEntries.close();
3105
+ }
3106
+ },
3107
+ children: dirty ? "Discard" : "Close"
3108
+ }
3109
+ ) })
3110
+ ] }),
3111
+ /* @__PURE__ */ jsxs("div", { className: "px-4 pb-4", children: [
3112
+ errorMessage ? /* @__PURE__ */ jsx("div", { className: "mb-3 rounded-md border border-red-500 bg-red-50 px-3 py-2 text-sm text-red-700", children: errorMessage }) : isSaving ? /* @__PURE__ */ jsx("div", { className: "mb-3 rounded-md border border-sky-500 bg-sky-50 px-3 py-2 text-sm text-sky-700", children: "Saving changes" }) : null,
3113
+ /* @__PURE__ */ jsx(
3114
+ PreviewFormProvider,
3115
+ {
3116
+ siteId,
3117
+ theme: theme ?? null,
3118
+ previewClient: apiClient,
3119
+ bearerToken,
3120
+ supabaseUrl,
3121
+ mediaAssets,
3122
+ children: /* @__PURE__ */ jsx(
3123
+ ManifestForm,
3124
+ {
3125
+ manifest,
3126
+ backend,
3127
+ widgetOverrides: widgets,
3128
+ className: "space-y-4"
3129
+ }
3130
+ )
3131
+ }
3132
+ )
3133
+ ] })
3134
+ ] });
3135
+ }
3136
+ function buildLinkedEntrySidebarSection(input) {
3137
+ const { snapshot, inspection, renderContent } = input;
3138
+ if (!snapshot || inspection.kind !== "linked-entry") return null;
3139
+ if (snapshot.meta.entryId !== inspection.entryId) return null;
3140
+ return {
3141
+ label: snapshot.meta.contentTypeLabel,
3142
+ openRequestId: inspection.requestId,
3143
+ content: renderContent(snapshot)
3144
+ };
3145
+ }
3146
+ function findLinkedEntryFieldsFromSnapshot(snapshot) {
3147
+ return snapshot.meta.fields.flatMap((field) => {
3148
+ if (typeof field.id !== "string" || typeof field.type !== "string") {
3149
+ return [];
3150
+ }
3151
+ return [{
3152
+ id: field.id,
3153
+ type: field.type,
3154
+ mediaKinds: Array.isArray(field.mediaKinds) ? field.mediaKinds.filter((item) => typeof item === "string") : null,
3155
+ fileKinds: Array.isArray(field.fileKinds) ? field.fileKinds.filter((item) => typeof item === "string") : null
3156
+ }];
3157
+ });
3158
+ }
3159
+ function fieldIdToLabel(fieldId) {
3160
+ return fieldId.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[-_\s]+/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join(" ");
3161
+ }
3162
+
3163
+ export { PreviewEditorSidebar };