@riverbankcms/sdk 0.104.3 → 0.108.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 (302) hide show
  1. package/README.md +84 -51
  2. package/dist/{PreviewEditorSidebar-CBMhU9p_.mjs → PreviewEditorSidebar-BBT-kZAM.mjs} +1850 -1183
  3. package/dist/{PreviewEditorUI-C5KEw4M8.mjs → PreviewEditorUI-D69xNKT2.mjs} +10 -9
  4. package/dist/{PreviewQueryProvider-CStABBUh.mjs → PreviewQueryProvider-RRu7wyQ5.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-Iq12HjL7.mjs → SdkPreviewModeRuntime-CuDjXH6o.mjs} +2562 -457
  6. package/dist/{SiteChromeCustomizeContext-Cx0NMdyv.mjs → SiteChromeCustomizeContext-BQUY2dEk.mjs} +7689 -3196
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +36493 -1395
  8. package/dist/_dts/api/src/blocks.d.ts +41 -7
  9. package/dist/_dts/api/src/bookingConfig.d.ts +2 -0
  10. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +5 -1
  11. package/dist/_dts/api/src/bookingRecords.d.ts +180 -115
  12. package/dist/_dts/api/src/cache-tags.d.ts +3 -0
  13. package/dist/_dts/api/src/contentRuntime.d.ts +25 -16
  14. package/dist/_dts/api/src/courses.d.ts +68 -10
  15. package/dist/_dts/api/src/endpoints.d.ts +128 -0
  16. package/dist/_dts/api/src/footer-settings.d.ts +1 -0
  17. package/dist/_dts/api/src/footerSettings.d.ts +8 -0
  18. package/dist/_dts/api/src/index.d.ts +127 -124
  19. package/dist/_dts/api/src/navigation/contracts.d.ts +3 -3
  20. package/dist/_dts/api/src/previewContentContract.d.ts +4 -0
  21. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +9 -0
  22. package/dist/_dts/api/src/sdkContracts.d.ts +178 -6
  23. package/dist/_dts/api/src/siteBanner.d.ts +2 -2
  24. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +143 -55
  25. package/dist/_dts/api/src/siteOperations.d.ts +1 -0
  26. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +32 -4
  27. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +72 -47
  28. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +36 -4
  29. package/dist/_dts/blocks/src/definitions.d.ts +1 -1
  30. package/dist/_dts/blocks/src/portal/types.d.ts +25 -7
  31. package/dist/_dts/blocks/src/primitives/layout/Section.d.ts +0 -1
  32. package/dist/_dts/blocks/src/rendering/server.d.ts +1 -0
  33. package/dist/_dts/blocks/src/system/blocks/course-registration.d.ts +2 -2
  34. package/dist/_dts/blocks/src/system/blocks/courses/shared/schemas.d.ts +2 -0
  35. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +2 -0
  36. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +4 -0
  37. package/dist/_dts/blocks/src/system/blocks/events/recurring-schedule-projection.d.ts +52 -0
  38. package/dist/_dts/blocks/src/system/blocks/events/recurring-schedule.d.ts +22 -0
  39. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +45 -0
  40. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +10 -0
  41. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +4 -1
  42. package/dist/_dts/blocks/src/system/blocks/index.d.ts +1 -0
  43. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +9 -0
  44. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +8 -2
  45. package/dist/_dts/blocks/src/system/blocks/offerings-preview/index.d.ts +8 -8
  46. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +24 -24
  47. package/dist/_dts/blocks/src/system/blocks/offerings-preview/scheduleSummary.d.ts +29 -0
  48. package/dist/_dts/blocks/src/system/blocks/shop.d.ts +2 -1
  49. package/dist/_dts/blocks/src/system/blocks/site-footer-runtime.d.ts +50 -0
  50. package/dist/_dts/blocks/src/system/blocks/site-footer.d.ts +9 -48
  51. package/dist/_dts/blocks/src/system/constants/background.d.ts +1 -1
  52. package/dist/_dts/blocks/src/system/constants/blockKinds.d.ts +1 -1
  53. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +2 -5
  54. package/dist/_dts/blocks/src/system/data/loaderEndpoints.d.ts +1 -1
  55. package/dist/_dts/blocks/src/system/designCapabilities.d.ts +1 -0
  56. package/dist/_dts/blocks/src/system/fragments/library/newsletterForm.d.ts +2 -2
  57. package/dist/_dts/blocks/src/system/icons/registry.d.ts +2 -1
  58. package/dist/_dts/blocks/src/system/icons/social.d.ts +5 -0
  59. package/dist/_dts/blocks/src/system/icons/svg.d.ts +1 -0
  60. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +2 -2
  61. package/dist/_dts/blocks/src/system/node/fragments/backgroundLayer.d.ts +13 -0
  62. package/dist/_dts/blocks/src/system/node/fragments/footerNav.d.ts +2 -0
  63. package/dist/_dts/blocks/src/system/node/fragments/styledCard.d.ts +3 -0
  64. package/dist/_dts/blocks/src/system/node/fragments/styledContainer.d.ts +4 -4
  65. package/dist/_dts/blocks/src/system/node/fragments/styledSection.d.ts +3 -12
  66. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +1 -1
  67. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  68. package/dist/_dts/blocks/src/system/runtime/blockComposition.d.ts +2 -0
  69. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +90 -6
  70. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +45 -3
  71. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +1 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/commerce/shared/display.d.ts +2 -2
  73. package/dist/_dts/blocks/src/system/runtime/nodes/course-details/logic.d.ts +2 -1
  74. package/dist/_dts/blocks/src/system/runtime/nodes/course-details/view.d.ts +1 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/course-details.server.d.ts +2 -1
  76. package/dist/_dts/blocks/src/system/runtime/nodes/course-page-surface-scope.d.ts +17 -0
  77. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/types.d.ts +3 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration.interactive.d.ts +5 -0
  79. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration.server.d.ts +1 -1
  80. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventScope.d.ts +9 -18
  81. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +106 -0
  82. package/dist/_dts/blocks/src/system/runtime/nodes/events/RecurringSchedule.d.ts +11 -0
  83. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +7 -3
  84. package/dist/_dts/blocks/src/system/runtime/nodes/media.d.ts +3 -0
  85. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.interactive.d.ts +11 -1
  86. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.server.d.ts +1 -1
  87. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +3 -0
  88. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview.d.ts +6 -6
  89. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -1
  90. package/dist/_dts/blocks/src/system/runtime/nodes/shop.interactive.d.ts +11 -5
  91. package/dist/_dts/blocks/src/system/runtime/nodes/shop.types.d.ts +2 -1
  92. package/dist/_dts/blocks/src/system/runtime/nodes/svg.d.ts +7 -0
  93. package/dist/_dts/blocks/src/system/types/link.d.ts +3 -3
  94. package/dist/_dts/content-editor/src/FooterComposerPanel.d.ts +14 -0
  95. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +27 -8
  96. package/dist/_dts/content-editor/src/footerComposer.d.ts +56 -0
  97. package/dist/_dts/content-editor/src/hooks/useEntryActions.d.ts +1 -1
  98. package/dist/_dts/content-editor/src/hooks/useEntryMutations.d.ts +5 -5
  99. package/dist/_dts/content-editor/src/index.d.ts +2 -0
  100. package/dist/_dts/content-editor/src/primitives/PreviewRichTextEditor.d.ts +3 -1
  101. package/dist/_dts/content-editor/src/services/contentEntryServices.d.ts +20 -36
  102. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +5 -1
  103. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +11 -0
  104. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  105. package/dist/_dts/content-model/src/definitions/shared.d.ts +3 -0
  106. package/dist/_dts/core/src/branded.d.ts +47 -0
  107. package/dist/_dts/core/src/dated-offering-settlement.d.ts +47 -0
  108. package/dist/_dts/core/src/editor-state.d.ts +4 -0
  109. package/dist/_dts/core/src/event-page-surface-subject.d.ts +44 -0
  110. package/dist/_dts/core/src/footer-composition.d.ts +143 -0
  111. package/dist/_dts/core/src/index.d.ts +51 -47
  112. package/dist/_dts/core/src/public-profile.d.ts +73 -0
  113. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +4398 -2859
  114. package/dist/_dts/db/src/schemas/forms.d.ts +10 -10
  115. package/dist/_dts/editor/src/primitives/rich-text/RichTextEditorSurface.d.ts +5 -0
  116. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +1 -0
  117. package/dist/_dts/editor-blocks/src/block-editor/FeatureCardIconsPanel.d.ts +1 -1
  118. package/dist/_dts/editor-blocks/src/block-editor/GeneratedIconSetDialog.d.ts +4 -1
  119. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockCreation.d.ts +2 -1
  120. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockDuplication.d.ts +2 -1
  121. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockReorder.d.ts +2 -1
  122. package/dist/_dts/editor-blocks/src/block-editor/owner.d.ts +10 -0
  123. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +1 -0
  124. package/dist/_dts/media-core/src/index.d.ts +2 -1
  125. package/dist/_dts/media-core/src/storagePathDomain.d.ts +47 -0
  126. package/dist/_dts/media-core/src/url.d.ts +1 -21
  127. package/dist/_dts/path-utils/src/index.d.ts +5 -0
  128. package/dist/_dts/preview-next/src/client/blocks/pageDesignRuntimePreview.d.ts +4 -2
  129. package/dist/_dts/preview-next/src/client/blocks/previewProjection/runtimeContentDecoration.d.ts +2 -1
  130. package/dist/_dts/preview-next/src/client/preview/PageBlockEditorPanel.d.ts +2 -1
  131. package/dist/_dts/preview-next/src/client/preview/PreviewChromeLayer.d.ts +5 -0
  132. package/dist/_dts/preview-next/src/client/preview/PreviewEditorSidebar.d.ts +4 -9
  133. package/dist/_dts/preview-next/src/client/preview/PreviewEntryControllerProvider.d.ts +1 -0
  134. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +8 -11
  135. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +4 -9
  136. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +2 -6
  137. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.d.ts +5 -9
  138. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.state.d.ts +81 -43
  139. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.typography.d.ts +61 -0
  140. package/dist/_dts/preview-next/src/client/preview/previewErrorMessages.d.ts +4 -0
  141. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +7 -17
  142. package/dist/_dts/preview-next/src/client/preview/styleConfiguratorSnapshot.d.ts +15 -8
  143. package/dist/_dts/preview-next/src/client/preview/typographyFontCatalog.d.ts +57 -0
  144. package/dist/_dts/sdk/src/cli/commands/pull.d.ts +1 -1
  145. package/dist/_dts/sdk/src/cli/commands/push/execute/theme.d.ts +4 -7
  146. package/dist/_dts/sdk/src/cli/commands/style.d.ts +24 -10
  147. package/dist/_dts/sdk/src/cli/content/reader.d.ts +1 -0
  148. package/dist/_dts/sdk/src/cli/site-commands/oneOffCommands.d.ts +3 -3
  149. package/dist/_dts/sdk/src/cli/sync/media.d.ts +16 -2
  150. package/dist/_dts/sdk/src/client/management/blocks.d.ts +14 -1
  151. package/dist/_dts/sdk/src/client/management/index.d.ts +2 -1
  152. package/dist/_dts/sdk/src/client/management/types.d.ts +147 -24
  153. package/dist/_dts/sdk/src/client/types.d.ts +2 -0
  154. package/dist/_dts/sdk/src/config/content-types.d.ts +2 -0
  155. package/dist/_dts/sdk/src/config/content-validation.d.ts +4 -0
  156. package/dist/_dts/sdk/src/config/types.d.ts +1 -1
  157. package/dist/_dts/sdk/src/config/validation.d.ts +3 -0
  158. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  159. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +23 -3
  160. package/dist/_dts/sdk/src/data/dataLoaderContext.d.ts +17 -0
  161. package/dist/_dts/sdk/src/data/dataLoaderValue.d.ts +36 -0
  162. package/dist/_dts/sdk/src/data/defineOwnDataProperty.d.ts +2 -0
  163. package/dist/_dts/sdk/src/data/executeCodeLoaders.d.ts +36 -40
  164. package/dist/_dts/sdk/src/data/index.d.ts +3 -3
  165. package/dist/_dts/sdk/src/data/resolveBlockData.d.ts +54 -0
  166. package/dist/_dts/sdk/src/data/types.d.ts +43 -78
  167. package/dist/_dts/sdk/src/navigation/index.d.ts +2 -0
  168. package/dist/_dts/sdk/src/next/index.d.ts +1 -0
  169. package/dist/_dts/sdk/src/next/redirect-destination.d.ts +1 -1
  170. package/dist/_dts/sdk/src/next/resolveTheme.d.ts +2 -1
  171. package/dist/_dts/sdk/src/next/types.d.ts +10 -1
  172. package/dist/_dts/sdk/src/public-api/contracts.d.ts +6 -10
  173. package/dist/_dts/sdk/src/rendering/components/Layout.d.ts +2 -1
  174. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +10 -5
  175. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +6 -10
  176. package/dist/_dts/sdk/src/rendering/index.d.ts +1 -0
  177. package/dist/_dts/sdk/src/server.d.ts +4 -3
  178. package/dist/_dts/sdk/src/types/block-kinds.d.ts +1 -1
  179. package/dist/_dts/sdk/src/version.d.ts +1 -1
  180. package/dist/_dts/site-commands/src/capabilityGaps.d.ts +2 -2
  181. package/dist/_dts/site-commands/src/commands.d.ts +934 -40
  182. package/dist/_dts/theme-core/src/accordions/types.d.ts +2 -2
  183. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +5 -1
  184. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +22 -0
  185. package/dist/_dts/theme-core/src/buttons/personalities/quiet-arrow.d.ts +17 -0
  186. package/dist/_dts/theme-core/src/buttons/personalities/two-tone-pill.d.ts +17 -0
  187. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +3 -3
  188. package/dist/_dts/theme-core/src/buttons/types.d.ts +12 -8
  189. package/dist/_dts/theme-core/src/cards/types.d.ts +3 -3
  190. package/dist/_dts/theme-core/src/data.d.ts +12 -0
  191. package/dist/_dts/theme-core/src/fonts/index.d.ts +1 -1
  192. package/dist/_dts/theme-core/src/fonts/types.d.ts +6 -0
  193. package/dist/_dts/theme-core/src/footer/mode.d.ts +15 -0
  194. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  195. package/dist/_dts/theme-core/src/interactive/baseSchema.d.ts +1 -1
  196. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +72 -54
  197. package/dist/_dts/theme-core/src/navigation/types.d.ts +1 -1
  198. package/dist/_dts/theme-core/src/palette/colors.d.ts +12 -0
  199. package/dist/_dts/theme-core/src/palette/curated/brand-led.d.ts +10 -5
  200. package/dist/_dts/theme-core/src/palette/curated/high-contrast.d.ts +6 -3
  201. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +31 -236
  202. package/dist/_dts/theme-core/src/palette/curated/soft-natural.d.ts +8 -4
  203. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +14 -12
  204. package/dist/_dts/theme-core/src/palette/curated/warm-dark.d.ts +4 -2
  205. package/dist/_dts/theme-core/src/palette/curated/warm-neutral.d.ts +8 -4
  206. package/dist/_dts/theme-core/src/palette/editorState.d.ts +33 -3
  207. package/dist/_dts/theme-core/src/palette/index.d.ts +0 -4
  208. package/dist/_dts/theme-core/src/palette/libraryModel.d.ts +12 -6
  209. package/dist/_dts/theme-core/src/palette/persistenceReadSource.d.ts +20 -72
  210. package/dist/_dts/theme-core/src/palette/resolveSelection.d.ts +29 -4
  211. package/dist/_dts/theme-core/src/palette/selection.d.ts +6 -4
  212. package/dist/_dts/theme-core/src/schema.d.ts +1307 -38
  213. package/dist/_dts/theme-core/src/site-styles/appearancePresetCatalog.d.ts +218 -158
  214. package/dist/_dts/theme-core/src/site-styles/appearancePresetTypes.d.ts +5 -6
  215. package/dist/_dts/theme-core/src/site-styles/blockAppearancePresets.d.ts +218 -158
  216. package/dist/_dts/theme-core/src/site-styles/compositionBudget.d.ts +1 -0
  217. package/dist/_dts/theme-core/src/site-styles/contentTemplateRuntimeCompiler.d.ts +2 -1
  218. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +161 -51
  219. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +6 -0
  220. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +4 -2
  221. package/dist/_dts/theme-core/src/site-styles/headerLooks.d.ts +12 -1
  222. package/dist/_dts/theme-core/src/site-styles/index.d.ts +1 -1
  223. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +24 -1
  224. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +14 -48
  225. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +10 -23
  226. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +15 -0
  227. package/dist/_dts/theme-core/src/site-styles/siteStyleThemeCompiler.d.ts +1 -2
  228. package/dist/_dts/theme-core/src/site-styles/siteStyleTypography.d.ts +456 -0
  229. package/dist/_dts/theme-core/src/site-styles/types.d.ts +52 -3
  230. package/dist/cli/index.mjs +4118 -2278
  231. package/dist/client/{DiscountCodeDisclosure-BXM73_E5.mjs → DiscountCodeDisclosure-D6TOf6Hi.mjs} +1 -1
  232. package/dist/client/{EventCombined.client-DT5elP7j.mjs → EventCombined.client-Dao3zxRk.mjs} +3 -3
  233. package/dist/client/{EventListing.client-pTYGfV9C.mjs → EventListing.client-B2NNt4y1.mjs} +2 -2
  234. package/dist/client/{LeafletStaticMap-dQrhlUe3.mjs → LeafletStaticMap-BhwFo16w.mjs} +1 -1
  235. package/dist/client/{MagicLinkForm-u6Dul4j5.mjs → MagicLinkForm-BWnB7cMR.mjs} +2 -2
  236. package/dist/client/{StepIndicator-nWqineJ2.mjs → StepIndicator-TzRiQ0BY.mjs} +2 -2
  237. package/dist/client/{accordion-CSJcTUyH.mjs → accordion-BPy-AJ4_.mjs} +1 -1
  238. package/dist/client/{bookingForm-C73wOuVa.mjs → bookingForm-BL4PGZds.mjs} +7 -7
  239. package/dist/client/bookings.mjs +382 -204
  240. package/dist/client/{buttons-DfYyqt8C.mjs → buttons-EBjyslaX.mjs} +265 -152
  241. package/dist/client/{carousel-fMBb0JSc.mjs → carousel-C2b7Cn9-.mjs} +1 -1
  242. package/dist/client/{cart-BtaCwT3S.mjs → cart-BQcbXIVD.mjs} +3 -3
  243. package/dist/client/{checkout-DkTwZSnh.mjs → checkout-DEmpGRJu.mjs} +3 -3
  244. package/dist/client/client.mjs +5019 -1886
  245. package/dist/client/{colorStyles-Wyl6JujD.mjs → colorStyles-jZS6KfkP.mjs} +1 -1
  246. package/dist/client/{colorTokenParts-dT8MISgJ.mjs → colorTokenParts-CMugwvX_.mjs} +3 -1
  247. package/dist/client/{courseRegistration-DnLmapJu.mjs → courseRegistration-BqRZPmsq.mjs} +56 -21
  248. package/dist/client/{eventCalendar-BvLOc9KY.mjs → eventCalendar-B8N_tSl5.mjs} +29 -21
  249. package/dist/client/{eventCombined-kShgJPQr.mjs → eventCombined-Gjn6gtq7.mjs} +2 -2
  250. package/dist/client/{eventDetails-C5NTM0Vo.mjs → eventDetails-M-VCxA-r.mjs} +4 -4
  251. package/dist/client/{eventListing-B82ZdKP6.mjs → eventListing-DR19hqYi.mjs} +2 -2
  252. package/dist/client/{eventRegistration-qJVFqBWo.mjs → eventRegistration-BR02a0k3.mjs} +8 -8
  253. package/dist/client/{form-BvAcKiuu.mjs → form-LY2xIbwg.mjs} +2 -2
  254. package/dist/client/{headerSection-NvAiEHAg.mjs → headerSection-CYwwXJfh.mjs} +3 -3
  255. package/dist/client/hooks.mjs +3335 -2210
  256. package/dist/client/{locationMap-D0j52xfI.mjs → locationMap-DWnBXrFq.mjs} +3 -3
  257. package/dist/client/{media-BCW_g2cQ.mjs → media-DHtSxS5V.mjs} +61 -56
  258. package/dist/client/{newsletterForm-B8_i93qH.mjs → newsletterForm-D9s-fQ2P.mjs} +3 -3
  259. package/dist/client/{pricing-DIYnyHOk.mjs → pricing-DDq80oYc.mjs} +1 -1
  260. package/dist/client/{productDetail-w0eAHkuQ.mjs → productDetail-B0SbWd45.mjs} +3 -3
  261. package/dist/client/{productList-Vsguq5Xt.mjs → productList-PwYlZn0y.mjs} +3 -3
  262. package/dist/client/{propsDecoder-CD2pJ1WB.mjs → propsDecoder-BRWBiH7i.mjs} +225 -68
  263. package/dist/client/rendering/client.mjs +2266 -1267
  264. package/dist/client/rendering/islands.mjs +20 -20
  265. package/dist/client/rendering.mjs +5364 -1979
  266. package/dist/client/{shop-Dgxs-Q8F.mjs → shop-CMwZPYME.mjs} +8 -8
  267. package/dist/client/{shop-commerce-DJAhsTaT.mjs → shop-commerce-BuxsFate.mjs} +6 -6
  268. package/dist/client/{shop-commerce-store-CfCXltXW.mjs → shop-commerce-store-boYlEJ91.mjs} +1 -1
  269. package/dist/client/{shopShared-CmDcPXUt.mjs → shopShared-D2naaOes.mjs} +3 -3
  270. package/dist/client/{useDiscountCodeUi-CirrT2bM.mjs → useDiscountCodeUi-D9RHpVoj.mjs} +1 -1
  271. package/dist/client/{useEventFilters-CNZxlCcO.mjs → useEventFilters-igGiK1re.mjs} +6 -6
  272. package/dist/client/{usePolling-Baks_iBI.mjs → usePolling-CI7MbsFB.mjs} +2 -2
  273. package/dist/preview-next/before-render.mjs +218 -1
  274. package/dist/preview-next/client/runtime.mjs +4 -4
  275. package/dist/preview-next/middleware.mjs +218 -1
  276. package/dist/{previewAuthStore-CfquDtTh.mjs → previewAuthStore-a_cBD28m.mjs} +218 -1
  277. package/dist/{sdk-runtime-BfNiFeNJ.mjs → sdk-runtime-MBzD-GmP.mjs} +2 -2
  278. package/dist/{separator-BhAR_Xmf.mjs → separator-5ahRBC85.mjs} +1 -1
  279. package/dist/server/components.mjs +5218 -2529
  280. package/dist/server/config-validation.mjs +3112 -2210
  281. package/dist/server/config.mjs +3112 -2211
  282. package/dist/server/data.mjs +3758 -2277
  283. package/dist/server/index.mjs +224 -4
  284. package/dist/server/navigation.mjs +9 -3
  285. package/dist/server/next/middleware.mjs +1 -1
  286. package/dist/server/next.mjs +6468 -2749
  287. package/dist/server/page-converter.mjs +3524 -2635
  288. package/dist/server/prebuild.mjs +1 -1
  289. package/dist/server/rendering/server.mjs +5392 -2488
  290. package/dist/server/rendering.mjs +6241 -2851
  291. package/dist/server/routing.mjs +3414 -1826
  292. package/dist/server/server.mjs +4174 -2354
  293. package/dist/server/theme-bridge.mjs +9579 -8132
  294. package/dist/server/theme.mjs +313 -154
  295. package/dist/styles/index.css +1223 -31
  296. package/package.json +1 -1
  297. package/dist/_dts/preview-next/src/client/preview/typographySelectionInput.d.ts +0 -16
  298. package/dist/_dts/theme-core/src/palette/historicalThemePalette.d.ts +0 -44
  299. package/dist/_dts/theme-core/src/palette/legacyRuntimeProjection.d.ts +0 -55
  300. package/dist/_dts/theme-core/src/palette/legacySelection.d.ts +0 -45
  301. package/dist/_dts/theme-core/src/palette/paletteCompatibilityTransition.d.ts +0 -232
  302. package/dist/_dts/theme-core/src/site-styles/typographyPresets.d.ts +0 -234
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { $i as getFooterLayout, $l as CollapsibleContent, A as openRow, Ai as getCuratedSiteStyle, B as setPaletteColors, Bc as buttonPersonalities, Bd as Maximize2, Bt as ManifestForm, C as createStyleConfiguratorState, D as markApplyFailed, Dl as SelectItem, E as isPreviewingChange, Ed as Tablet, El as SelectContent, F as previewHeaderLayout, Fl as Input, Gt as createStoreBackend, H as syncStyleConfiguratorState, Ht as createManifestValidation, I as previewHeaderStyle, Il as Tabs, Ji as evaluateTypographyFontSlot, L as previewSiteStyle, Ll as TabsContent, M as previewButtonPersonality, Ml as Popover, Mt as deriveRouteMetadataPresentation, N as previewCuratedPalette, Nl as PopoverContent, Nt as resolvePageRouteSlug, O as markApplySaved, Oi as curatedSiteStyles, Ol as SelectTrigger, P as previewFooterLook, Pl as PopoverTrigger, Pt as resolveContentTitle, Pu as cn, Q as resolvePreviewSidebarTab, Qi as findFooterLook, Ql as Collapsible, R as previewTypographyPreset, Rd as Monitor, Rl as TabsList, S as cancelPreview, T as dismissWarnings, Tl as Select, U as toggleRow, Ut as FormRenderer, V as styleConfiguratorHistoryEntryForPersistedSelection, Vc as findButtonPersonality, Vd as LoaderCircle, W as typographySelectionSnapshotFromFlat, Xi as typographyPresets, Yi as findTypographyPreset, Z as createOptimisticActionRunner, Zi as requireEntriesForCuratedChoices, _ as usePreviewDesignCoordinator, _a as paletteColorsSchema, _t as useEditorUiStore, ad as assertNever$1, b as applyResolvedSelections, bd as X$1, c as selectionSnapshotFromApplySiteStyleSelectionResponse, d as buildSiteChromeLookModels, dt as useUISelector, du as DropdownMenuContent, ea as getHeaderLayout, en as listEntryPageContentSlots, eu as CollapsibleTrigger, f as cycleSiteChromeLookId, ff as ChevronRight, fu as DropdownMenuItem, ga as paletteColorsEqual, h as planSiteChromeLookSelection, ha as paletteColorEntries, hu as DropdownMenuTrigger, j as paletteMutationCapability, ju as Button, k as markApplySaving, kd as Smartphone, kl as SelectValue, l as planSiteStylePreviewChange, lu as Surface, m as planSiteChromeFooterLayoutSelection, ma as findCuratedPalette, mt as useEntryController, mu as DropdownMenuSeparator, n as useSiteChromeCustomizeOptional, p as cycleableSiteChromeOptions, pa as curatedPalettes, pf as ChevronLeft, pu as DropdownMenuLabel, s as buildApplySiteStyleSelectionBody, ta as getHeaderStyle, u as resolveStyleConfiguratorInitialSnapshot, ut as ContentFieldsForm, uu as DropdownMenu, vt as useEditorUiStoreInstance, w as deriveStyleConfiguratorCapabilities, x as applyTypographyDraftPatch, z as resetTypographyToSaved, zl as TabsTrigger, zu as unsafeAsEditorBlockId } from "./SiteChromeCustomizeContext-Cx0NMdyv.mjs";
3
- import { d as unwrapResponse, f as getDashboardUrl, r as usePreviewAuth } from "./previewAuthStore-CfquDtTh.mjs";
4
- import { S as isAdapterReferenceKind, T as CalloutDescription, _ as usePreviewBlockCustomizeSelectionOptional, b as previewWidgetOverrides, d as buildPreviewDashboardSetupUrl, f as resolvePreviewDashboardSetupHandoff, g as canInspectSiteStyle, h as canApplySiteStyle, m as resolvePreviewSidebarChromeModel, p as resolvePreviewEditorGuidanceModel, v as useBlockCustomizeControls, w as Callout, x as BlockEditor, y as PreviewFormProvider } from "./SdkPreviewModeRuntime-Iq12HjL7.mjs";
5
- import { t as Separator } from "./separator-BhAR_Xmf.mjs";
2
+ import { $l as Input, A as deriveStyleConfiguratorCapabilities, Au as DropdownMenuItem, B as previewButtonPersonality, Bd as X$1, Ca as curatedPalettes, Ct as useEntryController, D as committedPaletteForSnapshot, Da as paletteColorsEqual, Dt as useEditorUiStoreInstance, Du as Surface, E as cancelPreview, Ea as paletteColorEntries, Et as useEditorUiStore, F as markApplySaving, G as previewSitePalette, Gl as SelectTrigger, H as previewFooterLook, Hl as Select, Ht as resolvePageRouteSlug, I as openRow, If as Check, J as resetTypographyToSaved, K as previewSiteStyle, Kd as Tablet, Kl as SelectValue, L as paletteMutationCapability, M as isPreviewingChange, Mu as DropdownMenuSeparator, N as markApplyFailed, Nf as ChevronRight, Nu as DropdownMenuTrigger, O as createPalette, Oa as paletteColorsSchema, Ou as DropdownMenu, P as markApplySaved, Pf as ChevronLeft, Q as toggleRow, Ql as PopoverTrigger, Qt as FormRenderer, Qu as cn, R as palettePreviewProjection, T as applyTypographyDraftPatch, Ta as buildPaletteLibraryReadModel, Td as assertNever$1, U as previewHeaderLayout, Ui as getCuratedSiteStyle, Ul as SelectContent, Ut as resolveContentTitle, V as previewCuratedPalette, Vi as curatedSiteStyles, Vt as deriveRouteMetadataPresentation, W as previewHeaderStyle, Wl as SelectItem, X as styleConfiguratorHistoryEntryForPersistedSelection, Xd as SlidersHorizontal, Xl as Popover, Y as setPaletteColors, Yd as Smartphone, Yl as SearchInput, Yt as ManifestForm, Yu as Button, Z as syncStyleConfiguratorState, Zl as PopoverContent, Zt as createManifestValidation, _ as planSiteChromeFooterLayoutSelection, _u as CollapsibleContent, af as Monitor, at as createOptimisticActionRunner, bt as useUISelector, c as selectionSnapshotFromApplySiteStyleSelectionResponse, ca as findFooterLook, cf as LoaderCircle, d as PREVIEW_DESIGN_REFRESH_ERROR_MESSAGE, da as getHeaderStyle, dn as listEntryPageContentSlots, en as createStoreBackend, eu as Tabs, f as PREVIEW_DESIGN_SAVE_ERROR_MESSAGE, g as cycleableSiteChromeOptions, gu as Collapsible, h as cycleSiteChromeLookId, id as unsafeAsEditorBlockId, j as dismissWarnings, ju as DropdownMenuLabel, k as createStyleConfiguratorState, ku as DropdownMenuContent, l as resolveStyleConfiguratorInitialSnapshot, la as getFooterLayout, m as buildSiteChromeLookModels, n as useSiteChromeCustomizeOptional, nl as buttonPersonalities, nu as TabsList, ot as resolvePreviewSidebarTab, p as reportPreviewDesignError, q as reconcilePaletteLibraryActive, rl as findButtonPersonality, ru as TabsTrigger, s as buildApplySiteStyleSelectionBody, sa as requireEntriesForCuratedChoices, sf as Maximize2, st as FooterComposerPanel, tf as Plus, tu as TabsContent, u as PREVIEW_DESIGN_LOAD_ERROR_MESSAGE, ua as getHeaderLayout, v as planSiteChromeLookSelection, vu as CollapsibleTrigger, w as applyResolvedSelections, wa as findCuratedPalette, x as usePreviewDesignCoordinator, y as planSiteStylePreviewChange, yt as ContentFieldsForm, z as prepareStalePaletteRetry } from "./SiteChromeCustomizeContext-BQUY2dEk.mjs";
3
+ import { d as unwrapResponse, f as getDashboardUrl, r as usePreviewAuth } from "./previewAuthStore-a_cBD28m.mjs";
4
+ import { C as buildFontPreviewUrl, D as Callout, O as CalloutDescription, S as isAdapterReferenceKind, T as getSiteFontsStorageUrl, _ as usePreviewBlockCustomizeSelectionOptional, b as previewWidgetOverrides, d as buildPreviewDashboardSetupUrl, f as resolvePreviewDashboardSetupHandoff, g as canInspectSiteStyle, h as canApplySiteStyle, m as resolvePreviewSidebarChromeModel, p as resolvePreviewEditorGuidanceModel, v as useBlockCustomizeControls, w as generateFontFaceCss, x as BlockEditor, y as PreviewFormProvider } from "./SdkPreviewModeRuntime-CuDjXH6o.mjs";
5
+ import { t as Separator } from "./separator-5ahRBC85.mjs";
6
6
  import React, { forwardRef, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, useTransition } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { useQuery, useQueryClient } from "@tanstack/react-query";
@@ -1564,7 +1564,7 @@ function formatBlockKindLabel(blockKind) {
1564
1564
  }
1565
1565
  //#endregion
1566
1566
  //#region ../preview-next/src/client/preview/PageBlockEditorPanel.tsx
1567
- function PageBlockEditorPanel({ siteId, pageId, dashboardBaseUrl, theme, apiClient, bearerToken, supabaseUrl, blockItems, uiStoreForBlockEditor, blockEditorRef, onBlocksChange, metadataPanel, linkedEntrySection, designPanel, designOpenRequestId = null, sdkConfig, mediaAssets, bookingOfferingPresets, viewportMode, viewportWidth, zoom, onViewportModeChange, onViewportWidthChange, onZoomChange }) {
1567
+ function PageBlockEditorPanel({ siteId, pageId, dashboardBaseUrl, theme, apiClient, bearerToken, supabaseUrl, blockItems, uiStoreForBlockEditor, blockEditorRef, onBlocksChange, metadataPanel, linkedEntrySection, designPanel, designOpenRequestId = null, designTakeoverActive = false, sdkConfig, mediaAssets, bookingOfferingPresets, viewportMode, viewportWidth, zoom, onViewportModeChange, onViewportWidthChange, onZoomChange }) {
1568
1568
  const customizeSelection = usePreviewBlockCustomizeSelectionOptional();
1569
1569
  const [designOpen, setDesignOpen] = useState(false);
1570
1570
  const [customizeOpen, setCustomizeOpen] = useState(false);
@@ -1627,35 +1627,48 @@ function PageBlockEditorPanel({ siteId, pageId, dashboardBaseUrl, theme, apiClie
1627
1627
  bookingOfferingPresets,
1628
1628
  sdkConfig: sdkConfig ?? null,
1629
1629
  children: /* @__PURE__ */ jsxs("div", {
1630
- className: "min-h-0 flex-1 space-y-0 overflow-auto",
1630
+ className: `min-h-0 flex-1 space-y-0 ${designTakeoverActive ? "flex flex-col overflow-hidden" : "overflow-auto"}`,
1631
+ "data-design-takeover-active": designTakeoverActive ? "true" : "false",
1631
1632
  children: [
1632
- linkedEntrySection ? /* @__PURE__ */ jsx(PreviewSidebarSection, {
1633
- label: linkedEntrySection.label,
1634
- openRequestId: linkedEntrySection.openRequestId,
1635
- children: linkedEntrySection.content
1633
+ linkedEntrySection ? /* @__PURE__ */ jsx("div", {
1634
+ hidden: designTakeoverActive,
1635
+ "data-design-takeover-sibling": true,
1636
+ children: /* @__PURE__ */ jsx(PreviewSidebarSection, {
1637
+ label: linkedEntrySection.label,
1638
+ openRequestId: linkedEntrySection.openRequestId,
1639
+ children: linkedEntrySection.content
1640
+ })
1636
1641
  }) : null,
1637
1642
  designPanel ? /* @__PURE__ */ jsx(Collapsible, {
1638
- open: designOpen,
1639
- onOpenChange: setDesignOpen,
1643
+ className: designTakeoverActive ? "flex min-h-0 flex-1 flex-col overflow-hidden" : void 0,
1644
+ open: designTakeoverActive || designOpen,
1645
+ onOpenChange: (open) => {
1646
+ if (!designTakeoverActive) setDesignOpen(open);
1647
+ },
1640
1648
  children: /* @__PURE__ */ jsxs("div", {
1641
1649
  ref: designSectionRef,
1642
- className: "border-b",
1650
+ className: designTakeoverActive ? "flex min-h-0 flex-1 flex-col overflow-hidden" : "border-b",
1643
1651
  children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
1644
1652
  asChild: true,
1645
1653
  children: /* @__PURE__ */ jsxs("div", {
1646
- className: "flex cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40",
1647
- children: [/* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${designOpen ? "rotate-90" : ""}` }), /* @__PURE__ */ jsx("span", {
1654
+ className: `flex shrink-0 cursor-pointer items-center gap-2 px-4 py-3 hover:bg-accent/40 ${designTakeoverActive ? "border-b" : ""}`,
1655
+ children: [/* @__PURE__ */ jsx(ChevronRight, { className: `h-4 w-4 transition ${designTakeoverActive || designOpen ? "rotate-90" : ""}` }), /* @__PURE__ */ jsx("span", {
1648
1656
  className: "text-sm font-semibold",
1649
1657
  children: "Design"
1650
1658
  })]
1651
1659
  })
1652
- }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
1653
- className: "px-4 pb-4",
1654
- children: designPanel
1655
- }) })]
1660
+ }), /* @__PURE__ */ jsx(CollapsibleContent, {
1661
+ className: designTakeoverActive ? "min-h-0 flex-1 data-[state=open]:h-full data-[state=open]:overflow-hidden" : void 0,
1662
+ children: /* @__PURE__ */ jsx("div", {
1663
+ className: designTakeoverActive ? "h-full min-h-0" : "px-4 pb-4",
1664
+ children: designPanel
1665
+ })
1666
+ })]
1656
1667
  })
1657
1668
  }) : null,
1658
1669
  /* @__PURE__ */ jsx(Collapsible, {
1670
+ hidden: designTakeoverActive,
1671
+ "data-design-takeover-sibling": true,
1659
1672
  open: customizeOpen,
1660
1673
  onOpenChange: setCustomizeOpen,
1661
1674
  children: /* @__PURE__ */ jsxs("div", {
@@ -1682,6 +1695,8 @@ function PageBlockEditorPanel({ siteId, pageId, dashboardBaseUrl, theme, apiClie
1682
1695
  })
1683
1696
  }),
1684
1697
  /* @__PURE__ */ jsx(Collapsible, {
1698
+ hidden: designTakeoverActive,
1699
+ "data-design-takeover-sibling": true,
1685
1700
  open: contentOpen,
1686
1701
  onOpenChange: setContentOpen,
1687
1702
  children: /* @__PURE__ */ jsxs("div", {
@@ -1718,6 +1733,8 @@ function PageBlockEditorPanel({ siteId, pageId, dashboardBaseUrl, theme, apiClie
1718
1733
  })
1719
1734
  }),
1720
1735
  metadataPanel ? /* @__PURE__ */ jsx(Collapsible, {
1736
+ hidden: designTakeoverActive,
1737
+ "data-design-takeover-sibling": true,
1721
1738
  open: detailsOpen,
1722
1739
  onOpenChange: setDetailsOpen,
1723
1740
  children: /* @__PURE__ */ jsxs("div", {
@@ -2061,712 +2078,516 @@ function ContrastSwatch({ hex, label }) {
2061
2078
  });
2062
2079
  }
2063
2080
  //#endregion
2064
- //#region ../preview-next/src/client/preview/StyleConfigurator.tsx
2065
- const MAX_FONT_RESULTS_WHEN_SEARCHING = 12;
2066
- const MAX_FONT_RESULTS_WHEN_BROWSING = 8;
2067
- const PALETTE_BRAND_ROLES = [
2081
+ //#region ../preview-next/src/client/preview/typographyFontCatalog.ts
2082
+ const typographyFontCategories = [
2068
2083
  {
2069
- key: "primary",
2070
- label: "Primary"
2084
+ value: "all",
2085
+ label: "All"
2071
2086
  },
2072
2087
  {
2073
- key: "primaryForeground",
2074
- label: "Primary text"
2088
+ value: "sans-serif",
2089
+ label: "Sans"
2075
2090
  },
2076
2091
  {
2077
- key: "secondary",
2078
- label: "Secondary"
2092
+ value: "serif",
2093
+ label: "Serif"
2079
2094
  },
2080
2095
  {
2081
- key: "accent",
2082
- label: "Accent"
2083
- }
2084
- ];
2085
- const PALETTE_FEEDBACK_ROLES = [
2086
- {
2087
- key: "success",
2088
- label: "Success"
2096
+ value: "display",
2097
+ label: "Display"
2089
2098
  },
2090
2099
  {
2091
- key: "warning",
2092
- label: "Warning"
2100
+ value: "handwriting",
2101
+ label: "Handwriting"
2093
2102
  },
2094
2103
  {
2095
- key: "destructive",
2096
- label: "Destructive"
2104
+ value: "monospace",
2105
+ label: "Mono"
2097
2106
  }
2098
2107
  ];
2099
- const PALETTE_SURFACE_ROLES = [
2100
- {
2101
- key: "background",
2102
- label: "Background"
2103
- },
2104
- {
2105
- key: "surface",
2106
- label: "Surface"
2107
- },
2108
- {
2109
- key: "surfaceAlt",
2110
- label: "Alternate surface"
2111
- },
2112
- {
2113
- key: "muted",
2114
- label: "Muted surface"
2115
- },
2116
- {
2117
- key: "text",
2118
- label: "Text"
2119
- },
2120
- {
2121
- key: "border",
2122
- label: "Border"
2108
+ function buildTypographyFontCatalog(input) {
2109
+ const normalizedQuery = input.query.trim().toLocaleLowerCase();
2110
+ const site = input.siteFonts.map(siteFontOption).filter((option) => fontOptionMatches(option, normalizedQuery, input.category)).sort(compareFontOptionsAlphabetically);
2111
+ const siteFamilies = new Set(input.siteFonts.map((font) => familyKey(font.familyName)));
2112
+ return {
2113
+ site,
2114
+ google: input.googleFonts.map(googleFontOption).filter((option) => !siteFamilies.has(familyKey(option.family))).filter((option) => fontOptionMatches(option, normalizedQuery, input.category)).sort(compareGoogleOptions)
2115
+ };
2116
+ }
2117
+ function visibleTypographyFontOptions(input) {
2118
+ const initial = input.options.slice(0, input.limit);
2119
+ if (input.limit <= 0 || initial.some((option) => familyKey(option.family) === familyKey(input.selectedFamily))) return initial;
2120
+ const selected = input.options.find((option) => familyKey(option.family) === familyKey(input.selectedFamily));
2121
+ if (!selected) return initial;
2122
+ return [...initial.slice(0, Math.max(0, input.limit - 1)), selected];
2123
+ }
2124
+ function fontOptionForFamily(family, catalog) {
2125
+ const key = familyKey(primaryFontFamily(family));
2126
+ const site = catalog.siteFonts.find((font) => familyKey(font.familyName) === key);
2127
+ if (site) return siteFontOption(site);
2128
+ const google = catalog.googleFonts.find((font) => familyKey(font.family) === key);
2129
+ return google ? googleFontOption(google) : null;
2130
+ }
2131
+ function primaryFontFamily(cssFamily) {
2132
+ return cssFamily.split(",")[0]?.trim().replace(/^["']|["']$/gu, "") || cssFamily;
2133
+ }
2134
+ function siteFontOption(font) {
2135
+ return {
2136
+ source: "site",
2137
+ family: font.familyName,
2138
+ label: font.displayName ?? font.familyName,
2139
+ category: normalizeCategory(font.category),
2140
+ siteFont: font
2141
+ };
2142
+ }
2143
+ function googleFontOption(font) {
2144
+ return {
2145
+ source: "google",
2146
+ family: font.family,
2147
+ label: font.family,
2148
+ category: normalizeCategory(font.category),
2149
+ googleFont: font
2150
+ };
2151
+ }
2152
+ function normalizeCategory(category) {
2153
+ switch (category) {
2154
+ case "sans-serif":
2155
+ case "serif":
2156
+ case "display":
2157
+ case "handwriting":
2158
+ case "monospace": return category;
2159
+ default: return null;
2123
2160
  }
2124
- ];
2125
- function StyleConfigurator({ siteId, apiClient, theme, selectionId, selectionVersion = null, selectionUpdatedAt, initialSelections, headerSelectionContext, footerSelectionContext, expandedRowRequest = null, siteStyleAccess }) {
2126
- const router = useRouter();
2127
- const previewDesign = usePreviewDesignCoordinator();
2128
- const siteChromeCustomize = useSiteChromeCustomizeOptional();
2129
- const setSiteStyleStagedPreviewActive = siteChromeCustomize?.siteStyleHistory?.setStagedPreviewActive;
2130
- const { applyFailed, applyStarted, applySucceeded, clearPreview, previewTheme } = previewDesign;
2131
- const [isRefreshPending, startRefreshTransition] = useTransition();
2132
- const baseSnapshot = useMemo(() => resolveStyleConfiguratorInitialSnapshot({
2133
- theme,
2134
- initialSelections: {
2135
- ...initialSelections,
2136
- selectionVersion: initialSelections.selectionVersion ?? selectionVersion ?? 0,
2137
- selectionUpdatedAt: initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
2138
- },
2139
- headerSelectionContext,
2140
- footerSelectionContext
2141
- }), [
2142
- footerSelectionContext,
2143
- headerSelectionContext,
2144
- theme,
2145
- initialSelections,
2146
- selectionUpdatedAt,
2147
- selectionVersion
2148
- ]);
2149
- const [state, setState] = useState(() => baseSnapshot ? createStyleConfiguratorState(baseSnapshot) : null);
2150
- const hasLoadedState = state !== null;
2151
- const hasStagedSiteStylePreview = state ? isPreviewingChange(state) : false;
2152
- const stateRef = useRef(state);
2153
- const clearPreviewRef = useRef(clearPreview);
2154
- const runnerRef = useRef(createOptimisticActionRunner());
2155
- const appliedExpandedRowRequestIdRef = useRef(null);
2156
- useEffect(() => {
2157
- stateRef.current = state;
2158
- }, [state]);
2159
- useEffect(() => {
2160
- clearPreviewRef.current = clearPreview;
2161
- }, [clearPreview]);
2161
+ }
2162
+ function fontOptionMatches(option, normalizedQuery, category) {
2163
+ const matchesQuery = normalizedQuery.length === 0 || option.family.toLocaleLowerCase().includes(normalizedQuery) || option.label.toLocaleLowerCase().includes(normalizedQuery);
2164
+ const matchesCategory = category === "all" || option.category === category;
2165
+ return matchesQuery && matchesCategory;
2166
+ }
2167
+ function compareGoogleOptions(left, right) {
2168
+ if (left.source !== "google" || right.source !== "google") return compareFontOptionsAlphabetically(left, right);
2169
+ return (left.googleFont.popularity ?? Number.POSITIVE_INFINITY) - (right.googleFont.popularity ?? Number.POSITIVE_INFINITY) || compareFontOptionsAlphabetically(left, right);
2170
+ }
2171
+ function compareFontOptionsAlphabetically(left, right) {
2172
+ return left.label.localeCompare(right.label);
2173
+ }
2174
+ function familyKey(family) {
2175
+ return family.trim().toLocaleLowerCase();
2176
+ }
2177
+ //#endregion
2178
+ //#region ../preview-next/src/client/preview/StyleConfigurator.typography.tsx
2179
+ const typographyOverviewSurface = { kind: "overview" };
2180
+ function useTypographyFontCatalog(apiClient, siteId, enabled) {
2181
+ const [google, setGoogle] = useState({
2182
+ kind: "idle",
2183
+ value: []
2184
+ });
2185
+ const [site, setSite] = useState({
2186
+ kind: "idle",
2187
+ value: []
2188
+ });
2189
+ const googleRequestRef = useRef(null);
2190
+ const siteRequestRef = useRef(null);
2162
2191
  useEffect(() => {
2163
- if (!setSiteStyleStagedPreviewActive) return;
2164
- setSiteStyleStagedPreviewActive(hasStagedSiteStylePreview);
2192
+ if (!enabled) return;
2193
+ if (googleRequestRef.current?.apiClient === apiClient) return;
2194
+ let cancelled = false;
2195
+ googleRequestRef.current = {
2196
+ apiClient,
2197
+ status: "loading"
2198
+ };
2199
+ setGoogle((current) => ({
2200
+ kind: "loading",
2201
+ value: current.value
2202
+ }));
2203
+ async function loadGoogleFonts() {
2204
+ try {
2205
+ const value = unwrapResponse(await apiClient({ endpoint: "listFonts" }));
2206
+ if (!cancelled) {
2207
+ googleRequestRef.current = {
2208
+ apiClient,
2209
+ status: "loaded"
2210
+ };
2211
+ setGoogle({
2212
+ kind: "loaded",
2213
+ value: Array.isArray(value) ? value : []
2214
+ });
2215
+ }
2216
+ } catch (error) {
2217
+ if (!cancelled) {
2218
+ googleRequestRef.current = null;
2219
+ setGoogle({
2220
+ kind: "failed",
2221
+ value: [],
2222
+ message: errorMessage(error, "Unable to load Google Fonts.")
2223
+ });
2224
+ }
2225
+ }
2226
+ }
2227
+ loadGoogleFonts();
2165
2228
  return () => {
2166
- setSiteStyleStagedPreviewActive(false);
2229
+ cancelled = true;
2230
+ if (googleRequestRef.current?.status === "loading") googleRequestRef.current = null;
2167
2231
  };
2168
- }, [hasStagedSiteStylePreview, setSiteStyleStagedPreviewActive]);
2169
- useEffect(() => {
2170
- if (!baseSnapshot) return;
2171
- setState((current) => current ? syncStyleConfiguratorState(current, baseSnapshot) : createStyleConfiguratorState(baseSnapshot));
2172
- }, [baseSnapshot]);
2232
+ }, [apiClient, enabled]);
2173
2233
  useEffect(() => {
2174
- if (!state || !baseSnapshot) {
2175
- clearPreview("style-configurator");
2176
- return;
2177
- }
2178
- if (!isPreviewingChange(state)) {
2179
- clearPreview("style-configurator");
2180
- return;
2181
- }
2182
- const siteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
2183
- if (!siteStyle) {
2184
- clearPreview("style-configurator");
2185
- return;
2234
+ if (!enabled) return;
2235
+ if (siteRequestRef.current?.apiClient === apiClient && siteRequestRef.current.siteId === siteId) return;
2236
+ let cancelled = false;
2237
+ siteRequestRef.current = {
2238
+ apiClient,
2239
+ siteId,
2240
+ status: "loading"
2241
+ };
2242
+ setSite((current) => ({
2243
+ kind: "loading",
2244
+ value: current.value
2245
+ }));
2246
+ async function loadSiteFonts() {
2247
+ try {
2248
+ const value = unwrapResponse(await apiClient({
2249
+ endpoint: "listSiteFonts",
2250
+ params: { siteId }
2251
+ }));
2252
+ if (!cancelled) {
2253
+ siteRequestRef.current = {
2254
+ apiClient,
2255
+ siteId,
2256
+ status: "loaded"
2257
+ };
2258
+ setSite({
2259
+ kind: "loaded",
2260
+ value: Array.isArray(value?.fonts) ? value.fonts : []
2261
+ });
2262
+ }
2263
+ } catch (error) {
2264
+ if (!cancelled) {
2265
+ siteRequestRef.current = null;
2266
+ setSite({
2267
+ kind: "failed",
2268
+ value: [],
2269
+ message: errorMessage(error, "Unable to load your fonts.")
2270
+ });
2271
+ }
2272
+ }
2186
2273
  }
2187
- previewTheme("style-configurator", planSiteStylePreviewChange({
2188
- siteStyle,
2189
- selection: state.previewed,
2190
- baseTheme: theme,
2191
- headerSelectionContext,
2192
- footerSelectionContext
2193
- }).themeOverlay, "site-style");
2194
- }, [
2195
- state,
2196
- baseSnapshot,
2197
- clearPreview,
2198
- previewTheme,
2199
- headerSelectionContext,
2200
- footerSelectionContext,
2201
- theme
2202
- ]);
2203
- useEffect(() => {
2204
- const runner = runnerRef.current;
2274
+ loadSiteFonts();
2205
2275
  return () => {
2206
- runner.invalidate();
2207
- clearPreviewRef.current("style-configurator");
2276
+ cancelled = true;
2277
+ if (siteRequestRef.current?.status === "loading") siteRequestRef.current = null;
2208
2278
  };
2209
- }, []);
2210
- useEffect(() => {
2211
- if (!expandedRowRequest) return;
2212
- if (appliedExpandedRowRequestIdRef.current === expandedRowRequest.id) return;
2213
- setState((current) => {
2214
- if (!current) return current;
2215
- appliedExpandedRowRequestIdRef.current = expandedRowRequest.id;
2216
- return openRow(current, expandedRowForPreviewDesignRow(expandedRowRequest.row));
2217
- });
2218
- }, [expandedRowRequest, hasLoadedState]);
2219
- const handlePreviewSiteStyle = useCallback((siteStyleId) => {
2220
- setState((current) => {
2221
- if (!current) return current;
2222
- const next = previewSiteStyle(current, siteStyleId);
2223
- const siteStyle = getCuratedSiteStyle(siteStyleId);
2224
- if (!siteStyle) return next;
2225
- const result = planSiteStylePreviewChange({
2226
- siteStyle,
2227
- selection: next.previewed,
2228
- baseTheme: theme,
2229
- headerSelectionContext,
2230
- footerSelectionContext
2231
- });
2232
- return applyResolvedSelections(next, {
2233
- resolved: result.resolvedSelections,
2234
- ...result.resolvedSelections.typographySelection.kind === "custom" ? { typographySelection: {
2235
- kind: "custom",
2236
- typography: result.themeOverlay.typography
2237
- } } : {},
2238
- warnings: result.warnings
2239
- });
2240
- });
2241
2279
  }, [
2242
- footerSelectionContext,
2243
- headerSelectionContext,
2244
- theme
2280
+ apiClient,
2281
+ enabled,
2282
+ siteId
2245
2283
  ]);
2246
- const handlePreviewPersonality = useCallback((id) => {
2247
- setState((current) => current ? previewButtonPersonality(current, id) : current);
2248
- }, []);
2249
- const handlePreviewHeaderLayout = useCallback((id) => {
2250
- if (!id) return;
2251
- setState((current) => current ? previewHeaderLayout(current, id) : current);
2252
- }, []);
2253
- const handlePreviewHeaderStyle = useCallback((id) => {
2254
- if (!id) return;
2255
- setState((current) => current ? previewHeaderStyle(current, id) : current);
2256
- }, []);
2257
- const handlePreviewFooterLook = useCallback((id) => {
2258
- const plan = planSiteChromeLookSelection({
2259
- surface: "footer",
2260
- lookId: id
2261
- });
2262
- if (plan.kind !== "footer") return;
2263
- setState((current) => current ? previewFooterLook(current, plan.next.footerLookId) : current);
2264
- }, []);
2265
- const handlePreviewFooterLayout = useCallback((id) => {
2266
- if (!id) return;
2267
- setState((current) => {
2268
- if (!current) return current;
2269
- const siteStyle = getCuratedSiteStyle(current.previewed.siteStyleId);
2270
- if (!siteStyle) return current;
2271
- const models = buildSiteChromeLookModels({
2272
- state: current,
2273
- siteStyle,
2274
- headerSelectionContext,
2275
- footerSelectionContext
2276
- });
2277
- const nextLookId = planSiteChromeFooterLayoutSelection({
2278
- layoutId: id,
2279
- currentLookId: current.previewed.footerLookId ?? models.footer.renderedLookId
2280
- });
2281
- return nextLookId ? previewFooterLook(current, nextLookId) : current;
2282
- });
2283
- }, [footerSelectionContext, headerSelectionContext]);
2284
- const handlePreviewPalette = useCallback((id) => {
2285
- const curated = findCuratedPalette(id);
2286
- if (!curated) return;
2287
- setState((current) => current ? previewCuratedPalette(current, {
2288
- paletteId: curated.id,
2289
- colors: curated.colors,
2290
- mode: curated.mode
2291
- }) : current);
2292
- }, []);
2293
- const handlePreviewTypographyPreset = useCallback((id) => {
2294
- setState((current) => current ? previewTypographyPreset(current, id) : current);
2295
- }, []);
2296
- const patchTypography = useCallback((patch) => {
2297
- setState((current) => {
2298
- if (!current) return current;
2299
- const siteStyle = getCuratedSiteStyle(current.previewed.siteStyleId);
2300
- if (!siteStyle) return current;
2301
- return applyTypographyDraftPatch(current, planSiteStylePreviewChange({
2302
- siteStyle,
2303
- selection: current.previewed,
2304
- baseTheme: theme,
2305
- headerSelectionContext,
2306
- footerSelectionContext
2307
- }).themeOverlay.typography, patch);
2308
- });
2309
- }, [
2310
- footerSelectionContext,
2311
- headerSelectionContext,
2312
- theme
2313
- ]);
2314
- const handleHeadingTypographyChange = useCallback((patch) => {
2315
- patchTypography({
2316
- kind: "heading",
2317
- patch
2318
- });
2319
- }, [patchTypography]);
2320
- const handleBodyTypographyChange = useCallback((patch) => {
2321
- patchTypography({
2322
- kind: "body",
2323
- patch
2324
- });
2325
- }, [patchTypography]);
2326
- const handleTypographyScaleChange = useCallback((scale) => {
2327
- patchTypography({
2328
- kind: "scale",
2329
- scale
2330
- });
2331
- }, [patchTypography]);
2332
- const handleResetTypography = useCallback(() => {
2333
- setState((current) => current ? resetTypographyToSaved(current) : current);
2334
- }, []);
2335
- const handleTokenOverride = useCallback((role, hex) => {
2336
- setState((current) => {
2337
- if (!current) return current;
2338
- const palette = current.previewed.palette;
2339
- if (palette.kind === "invalid") return current;
2340
- if (!(role in palette.colors)) return current;
2341
- const nextColors = paletteColorsSchema.safeParse({
2342
- ...palette.colors,
2343
- [role]: hex
2344
- });
2345
- return nextColors.success ? setPaletteColors(current, nextColors.data) : current;
2346
- });
2347
- }, []);
2348
- const handleResetOverrides = useCallback(() => {
2349
- setState((current) => {
2350
- if (!current || current.previewed.palette.kind !== "migrated-curated") return current;
2351
- const curated = findCuratedPalette(current.previewed.palette.paletteId);
2352
- return curated ? setPaletteColors(current, curated.colors) : current;
2353
- });
2354
- }, []);
2355
- const handleToggleRow = useCallback((row) => {
2356
- setState((current) => current ? toggleRow(current, row) : current);
2357
- }, []);
2358
- const handleCancel = useCallback(() => {
2359
- setState((current) => current ? cancelPreview(current) : current);
2360
- }, []);
2361
- const handleDismissWarnings = useCallback(() => {
2362
- setState((current) => current ? dismissWarnings(current) : current);
2363
- }, []);
2364
- const handleApply = useCallback(async () => {
2365
- const live = stateRef.current;
2366
- if (!live || !isPreviewingChange(live)) return;
2367
- if (!deriveStyleConfiguratorCapabilities(live).canApplyStaged) return;
2368
- const previewed = live.previewed;
2369
- const siteStyle = getCuratedSiteStyle(previewed.siteStyleId);
2370
- if (!siteStyle) return;
2371
- const compiled = planSiteStylePreviewChange({
2372
- siteStyle,
2373
- selection: previewed,
2374
- baseTheme: theme,
2375
- headerSelectionContext,
2376
- footerSelectionContext
2377
- });
2378
- await runnerRef.current.run({
2379
- optimistic: () => {
2380
- applyStarted("style-configurator");
2381
- setState((current) => current ? markApplySaving(current) : current);
2382
- },
2383
- network: async () => {
2384
- const body = buildApplySiteStyleSelectionBody({
2385
- base: live.base,
2386
- previewed: {
2387
- ...previewed,
2388
- buttonPersonalityId: compiled.resolvedSelections.buttonPersonalityId,
2389
- headerLayoutId: compiled.resolvedSelections.headerLayoutId,
2390
- headerStyleId: compiled.resolvedSelections.headerStyleId,
2391
- headerLookId: compiled.resolvedSelections.headerLookId,
2392
- typographySelection: compiled.resolvedSelections.typographySelection.kind === "custom" ? {
2393
- kind: "custom",
2394
- typography: compiled.themeOverlay.typography
2395
- } : typographySelectionSnapshotFromFlat(compiled.resolvedSelections)
2396
- },
2397
- selectionId
2398
- });
2399
- if (!body.ok) throw new Error(paletteApplyPlanErrorMessage(body.error));
2400
- return unwrapResponse(await apiClient({
2401
- endpoint: "applySiteStyleSelection",
2402
- params: { siteId },
2403
- body: body.body
2404
- }));
2405
- },
2406
- onSuccess: (saved) => {
2407
- const persisted = selectionSnapshotFromApplySiteStyleSelectionResponse(saved, previewed);
2408
- const historyEntry = styleConfiguratorHistoryEntryForPersistedSelection(live, persisted);
2409
- setState((current) => current ? markApplySaved(current, persisted) : current);
2410
- if (historyEntry) siteChromeCustomize?.siteStyleHistory?.recordAppliedSelection(historyEntry);
2411
- applySucceeded("style-configurator");
2412
- startRefreshTransition(() => {
2413
- router.refresh();
2414
- });
2415
- },
2416
- onError: (error) => {
2417
- applyFailed("style-configurator");
2418
- const applyError = classifyStyleApplyError(error);
2419
- if (applyError.kind === "stale") startRefreshTransition(() => {
2420
- router.refresh();
2421
- });
2422
- const message = applyError.message;
2423
- setState((current) => current ? markApplyFailed(current, message, applyError.kind === "stale" ? "stale" : "retryable") : current);
2424
- }
2425
- });
2426
- }, [
2427
- apiClient,
2428
- footerSelectionContext,
2429
- headerSelectionContext,
2430
- applyFailed,
2431
- applyStarted,
2432
- applySucceeded,
2433
- router,
2434
- selectionId,
2435
- siteChromeCustomize?.siteStyleHistory,
2436
- siteId,
2437
- startRefreshTransition,
2438
- theme
2439
- ]);
2440
- if (!state || !baseSnapshot) return /* @__PURE__ */ jsx("p", {
2441
- className: "text-sm text-muted-foreground",
2442
- children: "Loading design controls..."
2443
- });
2444
- const applyAllowed = canApplySiteStyle(siteStyleAccess);
2445
- const stagedCapabilities = deriveStyleConfiguratorCapabilities(state);
2446
- const previewingChange = isPreviewingChange(state);
2447
- const saving = stagedCapabilities.isApplyingStaged || isRefreshPending;
2448
- const aggregateMutationControlsDisabled = saving || !stagedCapabilities.canPreviewStaged;
2449
- const currentSiteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
2450
- if (!currentSiteStyle) return /* @__PURE__ */ jsx("p", {
2451
- className: "text-sm text-muted-foreground",
2452
- children: "This site uses a Site Style that cannot be configured here."
2453
- });
2454
- const currentPersonality = findButtonPersonality(state.previewed.buttonPersonalityId);
2455
- const currentPalette = state.previewed.palette;
2456
- const paletteMutation = paletteMutationCapability(state);
2457
- const paletteControlsDisabled = saving || paletteMutation.kind !== "editable";
2458
- const currentCuratedPalette = currentPalette.kind === "migrated-curated" ? findCuratedPalette(currentPalette.paletteId) : null;
2459
- const currentTypographyPreset = state.previewed.typographySelection.kind === "preset" && state.previewed.typographySelection.presetId ? findTypographyPreset(state.previewed.typographySelection.presetId) : null;
2460
- const currentTypography = planSiteStylePreviewChange({
2461
- siteStyle: currentSiteStyle,
2462
- selection: state.previewed,
2463
- baseTheme: theme,
2464
- headerSelectionContext,
2465
- footerSelectionContext
2466
- }).themeOverlay.typography;
2467
- const chromeLookModels = buildSiteChromeLookModels({
2468
- state,
2469
- siteStyle: currentSiteStyle,
2470
- headerSelectionContext,
2471
- footerSelectionContext
2472
- });
2473
- const currentHeaderLayout = getHeaderLayout(state.previewed.headerLayoutId);
2474
- const currentHeaderStyle = getHeaderStyle(state.previewed.headerStyleId);
2475
- const currentFooterLayout = getFooterLayout(chromeLookModels.footerLayout.renderedLayoutId);
2476
- const currentFooterLook = state.previewed.footerLookId ? findFooterLook(state.previewed.footerLookId) : findFooterLook(chromeLookModels.footer.renderedLookId);
2477
- const headerSurfaceState = siteChromeCustomize?.getSurfaceState("header") ?? { kind: "unavailable" };
2284
+ return {
2285
+ google,
2286
+ site
2287
+ };
2288
+ }
2289
+ function TypographyOverviewContent({ typography, catalog, onChooseFont, onOpenAdvanced, onReset, disabled }) {
2478
2290
  return /* @__PURE__ */ jsxs("div", {
2479
2291
  className: "space-y-3",
2480
2292
  children: [
2481
- state.warnings.length > 0 ? /* @__PURE__ */ jsx(WarningNotice, {
2482
- warnings: state.warnings,
2483
- onDismiss: handleDismissWarnings
2484
- }) : null,
2485
- /* @__PURE__ */ jsx(SiteStyleRow, {
2486
- expanded: state.expandedRow === "style",
2487
- onToggle: () => handleToggleRow("style"),
2488
- current: currentSiteStyle,
2489
- onPreview: handlePreviewSiteStyle,
2490
- disabled: aggregateMutationControlsDisabled
2293
+ /* @__PURE__ */ jsxs("div", {
2294
+ className: "space-y-2",
2295
+ children: [/* @__PURE__ */ jsx(TypographyRoleSummary, {
2296
+ slot: "heading",
2297
+ family: typography.headings.default.family,
2298
+ catalog,
2299
+ onChoose: () => onChooseFont("heading"),
2300
+ disabled
2301
+ }), /* @__PURE__ */ jsx(TypographyRoleSummary, {
2302
+ slot: "body",
2303
+ family: typography.body.family,
2304
+ catalog,
2305
+ onChoose: () => onChooseFont("body"),
2306
+ disabled
2307
+ })]
2491
2308
  }),
2492
- /* @__PURE__ */ jsx(SiteChromeLookRow, {
2493
- label: "Header layout",
2494
- ariaLabel: "Header layout",
2495
- defaultDescription: "Choose the header structure.",
2496
- expanded: state.expandedRow === "header-layout",
2497
- onToggle: () => handleToggleRow("header-layout"),
2498
- currentId: state.previewed.headerLayoutId,
2499
- currentLabel: currentHeaderLayout?.label ?? state.previewed.headerLayoutId,
2500
- scopeDescription: "Changes the header structure on every page.",
2501
- options: chromeLookModels.headerLayout.options,
2502
- diagnostics: [],
2503
- onCycle: (direction) => {
2504
- const next = cycleSiteChromeLookId(chromeLookModels.headerLayout.options, state.previewed.headerLayoutId, direction);
2505
- if (next !== void 0) handlePreviewHeaderLayout(next);
2506
- },
2507
- onPreview: handlePreviewHeaderLayout,
2508
- disabled: aggregateMutationControlsDisabled
2309
+ /* @__PURE__ */ jsxs("div", {
2310
+ className: "flex items-center gap-2 border-t pt-3",
2311
+ children: [/* @__PURE__ */ jsxs(Button, {
2312
+ type: "button",
2313
+ size: "sm",
2314
+ variant: "outline",
2315
+ className: "flex-1 justify-between",
2316
+ onClick: onOpenAdvanced,
2317
+ disabled,
2318
+ children: [/* @__PURE__ */ jsxs("span", {
2319
+ className: "inline-flex items-center gap-2",
2320
+ children: [/* @__PURE__ */ jsx(SlidersHorizontal, {
2321
+ className: "h-3.5 w-3.5",
2322
+ "aria-hidden": "true"
2323
+ }), "Advanced typography"]
2324
+ }), /* @__PURE__ */ jsx(ChevronRight, {
2325
+ className: "h-3.5 w-3.5",
2326
+ "aria-hidden": "true"
2327
+ })]
2328
+ }), /* @__PURE__ */ jsx(Button, {
2329
+ type: "button",
2330
+ size: "sm",
2331
+ variant: "ghost",
2332
+ onClick: onReset,
2333
+ disabled,
2334
+ children: "Reset"
2335
+ })]
2509
2336
  }),
2510
- /* @__PURE__ */ jsx(SiteChromeLookRow, {
2511
- label: "Header look",
2512
- ariaLabel: "Header look",
2513
- defaultDescription: "Choose the header visual style.",
2514
- expanded: state.expandedRow === "header-style",
2515
- onToggle: () => handleToggleRow("header-style"),
2516
- currentId: state.previewed.headerStyleId,
2517
- currentLabel: currentHeaderStyle?.label ?? state.previewed.headerStyleId,
2518
- scopeDescription: "Changes the header surface and link style.",
2519
- options: chromeLookModels.headerStyle.options,
2520
- diagnostics: [],
2521
- onCycle: (direction) => {
2522
- const next = cycleSiteChromeLookId(chromeLookModels.headerStyle.options, state.previewed.headerStyleId, direction);
2523
- if (next !== void 0) handlePreviewHeaderStyle(next);
2524
- },
2525
- onPreview: handlePreviewHeaderStyle,
2526
- disabled: aggregateMutationControlsDisabled,
2527
- extraControls: isHeaderMenuSurfaceState(headerSurfaceState) ? /* @__PURE__ */ jsx(HeaderMenuControls, {
2528
- state: headerSurfaceState,
2529
- disabled: saving,
2530
- onMenuFontChange: siteChromeCustomize?.setHeaderMenuFont,
2531
- onMenuFontSizeChange: siteChromeCustomize?.setHeaderMenuFontSize
2532
- }) : null
2337
+ /* @__PURE__ */ jsx(SiteFontFaceStyles, { siteFonts: catalog.site.value })
2338
+ ]
2339
+ });
2340
+ }
2341
+ function TypographyRoleSummary({ slot, family, catalog, onChoose, disabled }) {
2342
+ const primaryFamily = primaryFontFamily(family);
2343
+ const option = fontOptionForFamily(primaryFamily, {
2344
+ googleFonts: catalog.google.value,
2345
+ siteFonts: catalog.site.value
2346
+ });
2347
+ const label = slot === "heading" ? "Heading" : "Body";
2348
+ return /* @__PURE__ */ jsxs("button", {
2349
+ type: "button",
2350
+ className: "group flex w-full items-center gap-3 rounded-md border bg-background px-3 py-3 text-left transition hover:border-foreground/30 hover:bg-accent/20 disabled:cursor-not-allowed disabled:opacity-50",
2351
+ "aria-label": `Choose ${label.toLocaleLowerCase()} font, currently ${primaryFamily}`,
2352
+ onClick: onChoose,
2353
+ disabled,
2354
+ children: [
2355
+ /* @__PURE__ */ jsx(FontPreviewText, {
2356
+ family: primaryFamily,
2357
+ source: option?.source ?? "unknown",
2358
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-muted/70 text-xl",
2359
+ children: slot === "heading" ? "Hh" : "Aa"
2533
2360
  }),
2534
- /* @__PURE__ */ jsx(SiteChromeLookRow, {
2535
- label: "Footer layout",
2536
- ariaLabel: "Footer layout",
2537
- defaultDescription: "Choose the footer structure.",
2538
- expanded: state.expandedRow === "footer-layout",
2539
- onToggle: () => handleToggleRow("footer-layout"),
2540
- currentId: chromeLookModels.footerLayout.renderedLayoutId,
2541
- currentLabel: currentFooterLayout?.label ?? chromeLookModels.footerLayout.renderedLayoutId,
2542
- scopeDescription: "Changes the footer structure on every page.",
2543
- options: chromeLookModels.footerLayout.options,
2544
- diagnostics: [],
2545
- onCycle: (direction) => {
2546
- const next = cycleSiteChromeLookId(chromeLookModels.footerLayout.options, chromeLookModels.footerLayout.renderedLayoutId, direction);
2547
- if (next !== void 0) handlePreviewFooterLayout(next);
2548
- },
2549
- onPreview: handlePreviewFooterLayout,
2550
- disabled: aggregateMutationControlsDisabled
2551
- }),
2552
- /* @__PURE__ */ jsx(SiteChromeLookRow, {
2553
- label: "Footer look",
2554
- ariaLabel: "Footer look",
2555
- defaultDescription: "Use the footer look chosen for this site style.",
2556
- expanded: state.expandedRow === "footer",
2557
- onToggle: () => handleToggleRow("footer"),
2558
- currentId: state.previewed.footerLookId,
2559
- currentLabel: state.previewed.footerLookId ? currentFooterLook?.label ?? "Unknown footer Look" : `${currentFooterLook?.label ?? "Site style"} (default)`,
2560
- scopeDescription: "Changes the footer on every page.",
2561
- options: chromeLookModels.footer.options,
2562
- diagnostics: chromeLookModels.footer.diagnostics,
2563
- onCycle: (direction) => {
2564
- const next = cycleSiteChromeLookId(chromeLookModels.footer.options, state.previewed.footerLookId, direction);
2565
- if (next !== void 0) handlePreviewFooterLook(next);
2566
- },
2567
- onPreview: handlePreviewFooterLook,
2568
- disabled: aggregateMutationControlsDisabled
2569
- }),
2570
- /* @__PURE__ */ jsx(PersonalityRow, {
2571
- expanded: state.expandedRow === "buttons",
2572
- onToggle: () => handleToggleRow("buttons"),
2573
- siteStyle: currentSiteStyle,
2574
- currentId: state.previewed.buttonPersonalityId,
2575
- currentLabel: currentPersonality?.name ?? state.previewed.buttonPersonalityId,
2576
- onPreview: handlePreviewPersonality,
2577
- disabled: aggregateMutationControlsDisabled
2361
+ /* @__PURE__ */ jsxs("span", {
2362
+ className: "min-w-0 flex-1",
2363
+ children: [/* @__PURE__ */ jsx("span", {
2364
+ className: "block text-xs font-medium text-muted-foreground",
2365
+ children: label
2366
+ }), /* @__PURE__ */ jsx(FontPreviewText, {
2367
+ family: primaryFamily,
2368
+ source: option?.source ?? "unknown",
2369
+ className: "block truncate text-base",
2370
+ children: primaryFamily
2371
+ })]
2578
2372
  }),
2579
- /* @__PURE__ */ jsx(TypographyRow, {
2580
- expanded: state.expandedRow === "typography",
2581
- onToggle: () => handleToggleRow("typography"),
2582
- apiClient,
2583
- siteStyle: currentSiteStyle,
2584
- typography: currentTypography,
2585
- currentPresetId: state.previewed.typographySelection.kind === "preset" ? state.previewed.typographySelection.presetId : null,
2586
- currentLabel: currentTypographyPreset?.name ?? "Custom typography",
2587
- onPreviewPreset: handlePreviewTypographyPreset,
2588
- onHeadingChange: handleHeadingTypographyChange,
2589
- onBodyChange: handleBodyTypographyChange,
2590
- onScaleChange: handleTypographyScaleChange,
2591
- onReset: handleResetTypography,
2592
- disabled: aggregateMutationControlsDisabled
2373
+ /* @__PURE__ */ jsx(ChevronRight, {
2374
+ className: "h-4 w-4 shrink-0 text-muted-foreground transition group-hover:text-foreground",
2375
+ "aria-hidden": "true"
2376
+ })
2377
+ ]
2378
+ });
2379
+ }
2380
+ function TypographyFontPickerTakeover({ slot, value, catalog, onBack, onChange, disabled }) {
2381
+ const [query, setQuery] = useState("");
2382
+ const [category, setCategory] = useState("all");
2383
+ const [visibleGoogleCount, setVisibleGoogleCount] = useState(24);
2384
+ const selectedFamily = primaryFontFamily(value);
2385
+ const catalogResult = useMemo(() => buildTypographyFontCatalog({
2386
+ googleFonts: catalog.google.value,
2387
+ siteFonts: catalog.site.value,
2388
+ query,
2389
+ category
2390
+ }), [
2391
+ catalog.google.value,
2392
+ catalog.site.value,
2393
+ category,
2394
+ query
2395
+ ]);
2396
+ const searching = query.trim().length > 0;
2397
+ const visibleGoogle = searching ? catalogResult.google : visibleTypographyFontOptions({
2398
+ options: catalogResult.google,
2399
+ selectedFamily,
2400
+ limit: visibleGoogleCount
2401
+ });
2402
+ const hasMore = !searching && visibleGoogle.length < catalogResult.google.length;
2403
+ const title = slot === "heading" ? "Heading font" : "Body font";
2404
+ useEffect(() => {
2405
+ setVisibleGoogleCount(24);
2406
+ }, [category, slot]);
2407
+ return /* @__PURE__ */ jsxs("section", {
2408
+ "aria-label": title,
2409
+ className: "flex min-h-0 flex-1 flex-col overflow-hidden bg-background",
2410
+ children: [
2411
+ /* @__PURE__ */ jsxs("header", {
2412
+ className: "shrink-0 space-y-3 border-b p-3",
2413
+ children: [
2414
+ /* @__PURE__ */ jsxs("div", {
2415
+ className: "flex items-center gap-2",
2416
+ children: [/* @__PURE__ */ jsx(Button, {
2417
+ type: "button",
2418
+ size: "icon",
2419
+ variant: "ghost",
2420
+ className: "h-8 w-8 shrink-0",
2421
+ onClick: onBack,
2422
+ "aria-label": "Back to typography",
2423
+ children: /* @__PURE__ */ jsx(ChevronLeft, {
2424
+ className: "h-4 w-4",
2425
+ "aria-hidden": "true"
2426
+ })
2427
+ }), /* @__PURE__ */ jsxs("div", {
2428
+ className: "min-w-0",
2429
+ children: [/* @__PURE__ */ jsx("h2", {
2430
+ className: "text-sm font-semibold",
2431
+ children: title
2432
+ }), /* @__PURE__ */ jsxs("p", {
2433
+ className: "truncate text-xs text-muted-foreground",
2434
+ children: ["Current: ", selectedFamily]
2435
+ })]
2436
+ })]
2437
+ }),
2438
+ /* @__PURE__ */ jsx(SearchInput, {
2439
+ value: query,
2440
+ onChange: (event) => setQuery(event.currentTarget.value),
2441
+ placeholder: "Search fonts",
2442
+ "aria-label": `Search ${slot} fonts`,
2443
+ disabled
2444
+ }),
2445
+ /* @__PURE__ */ jsx("div", {
2446
+ role: "group",
2447
+ "aria-label": "Font category",
2448
+ className: "flex gap-1 overflow-x-auto pb-0.5",
2449
+ children: typographyFontCategories.map((option) => /* @__PURE__ */ jsx(Button, {
2450
+ type: "button",
2451
+ size: "sm",
2452
+ variant: category === option.value ? "secondary" : "ghost",
2453
+ className: "h-7 shrink-0 px-2 text-xs",
2454
+ "aria-pressed": category === option.value,
2455
+ onClick: () => setCategory(option.value),
2456
+ disabled,
2457
+ children: option.label
2458
+ }, option.value))
2459
+ })
2460
+ ]
2593
2461
  }),
2594
- /* @__PURE__ */ jsx(PaletteRow, {
2595
- expanded: state.expandedRow === "palette",
2596
- onToggle: () => handleToggleRow("palette"),
2597
- siteStyle: currentSiteStyle,
2598
- currentId: currentCuratedPalette?.id ?? null,
2599
- currentLabel: currentCuratedPalette?.name ?? (currentPalette.kind === "migrated-site" ? "Site palette" : "Custom palette"),
2600
- currentSwatch: currentCuratedPalette?.colors.primary ?? null,
2601
- onPreview: handlePreviewPalette,
2602
- mutationCapability: paletteMutation,
2603
- disabled: paletteControlsDisabled
2462
+ /* @__PURE__ */ jsxs("div", {
2463
+ "data-typography-font-catalog-scroll": true,
2464
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-2",
2465
+ children: [
2466
+ /* @__PURE__ */ jsx(FontCatalogFeedback, { catalog }),
2467
+ catalogResult.site.length > 0 ? /* @__PURE__ */ jsx(FontOptionSection, {
2468
+ label: "Your fonts",
2469
+ children: catalogResult.site.map((option) => /* @__PURE__ */ jsx(TypographyFontOptionRow, {
2470
+ option,
2471
+ selected: familyMatches(selectedFamily, option.family),
2472
+ onSelect: () => onChange(option.family),
2473
+ disabled
2474
+ }, `site:${option.family}`))
2475
+ }) : null,
2476
+ visibleGoogle.length > 0 ? /* @__PURE__ */ jsxs(FontOptionSection, {
2477
+ label: searching ? "Matching fonts" : "Popular fonts",
2478
+ children: [visibleGoogle.map((option) => /* @__PURE__ */ jsx(TypographyFontOptionRow, {
2479
+ option,
2480
+ selected: familyMatches(selectedFamily, option.family),
2481
+ onSelect: () => onChange(option.family),
2482
+ disabled
2483
+ }, `google:${option.family}`)), hasMore ? /* @__PURE__ */ jsx(Button, {
2484
+ type: "button",
2485
+ size: "sm",
2486
+ variant: "ghost",
2487
+ className: "mt-1 w-full",
2488
+ onClick: () => setVisibleGoogleCount((count) => count + 24),
2489
+ disabled,
2490
+ children: "Show more fonts"
2491
+ }) : null]
2492
+ }) : catalog.google.kind === "loaded" && catalog.site.kind === "loaded" ? /* @__PURE__ */ jsx("p", {
2493
+ className: "px-2 py-8 text-center text-sm text-muted-foreground",
2494
+ children: "No fonts match your search."
2495
+ }) : null
2496
+ ]
2604
2497
  }),
2605
- currentPalette.kind !== "invalid" ? /* @__PURE__ */ jsx(ColoursRow, {
2606
- expanded: state.expandedRow === "colours",
2607
- onToggle: () => handleToggleRow("colours"),
2608
- palette: currentPalette.colors,
2609
- isCustom: !currentCuratedPalette || currentPalette.mode !== currentCuratedPalette.mode || !paletteColorsEqual(currentPalette.colors, currentCuratedPalette.colors),
2610
- onTokenChange: handleTokenOverride,
2611
- onReset: handleResetOverrides,
2612
- disabled: paletteControlsDisabled,
2613
- mutationCapability: paletteMutation
2614
- }) : null,
2615
- previewingChange ? /* @__PURE__ */ jsxs("div", {
2616
- className: "flex items-center gap-2 pt-2",
2617
- children: [/* @__PURE__ */ jsx(Button, {
2618
- type: "button",
2619
- size: "sm",
2620
- variant: "outline",
2621
- onClick: handleCancel,
2622
- disabled: !stagedCapabilities.canCancelStaged || saving,
2623
- children: "Cancel"
2624
- }), applyAllowed ? /* @__PURE__ */ jsx(Button, {
2625
- type: "button",
2626
- size: "sm",
2627
- onClick: handleApply,
2628
- disabled: !stagedCapabilities.canApplyStaged || saving,
2629
- children: saving ? "Applying…" : "Apply"
2630
- }) : /* @__PURE__ */ jsx("span", {
2631
- className: "text-xs text-muted-foreground",
2632
- children: "Preview only — ask an admin to apply."
2633
- })]
2634
- }) : null,
2635
- state.saveStatus.kind === "saved" ? /* @__PURE__ */ jsx("p", {
2636
- className: "text-xs text-success",
2637
- children: "Saved."
2638
- }) : null,
2639
- state.saveStatus.kind === "failed" ? /* @__PURE__ */ jsx("p", {
2640
- className: "text-xs text-destructive",
2641
- children: state.saveStatus.message
2642
- }) : null
2498
+ /* @__PURE__ */ jsx(SiteFontFaceStyles, { siteFonts: catalog.site.value })
2643
2499
  ]
2644
2500
  });
2645
2501
  }
2646
- function StyleRowFrame({ label, currentLabel, swatch, expanded, onToggle, children }) {
2647
- return /* @__PURE__ */ jsx(Collapsible, {
2648
- open: expanded,
2649
- onOpenChange: onToggle,
2650
- children: /* @__PURE__ */ jsxs("div", {
2651
- className: "rounded-md border bg-background",
2652
- children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
2653
- asChild: true,
2654
- children: /* @__PURE__ */ jsxs("button", {
2655
- type: "button",
2656
- className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-accent/30",
2657
- "aria-expanded": expanded,
2658
- children: [
2659
- /* @__PURE__ */ jsx(ChevronRight, { className: cn("h-4 w-4 transition", expanded && "rotate-90") }),
2660
- /* @__PURE__ */ jsxs("div", {
2661
- className: "flex-1 min-w-0",
2662
- children: [/* @__PURE__ */ jsx("div", {
2663
- className: "text-xs uppercase tracking-wide text-muted-foreground",
2664
- children: label
2665
- }), /* @__PURE__ */ jsx("div", {
2666
- className: "truncate text-sm font-medium",
2667
- children: currentLabel
2668
- })]
2669
- }),
2670
- swatch ? /* @__PURE__ */ jsx("span", {
2671
- className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
2672
- style: { backgroundColor: swatch },
2673
- "aria-hidden": "true"
2674
- }) : null
2675
- ]
2676
- })
2677
- }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
2678
- className: "border-t px-3 py-3",
2679
- children
2680
- }) })]
2681
- })
2502
+ function FontOptionSection({ label, children }) {
2503
+ return /* @__PURE__ */ jsxs("section", {
2504
+ "aria-label": label,
2505
+ className: "mb-3 last:mb-0",
2506
+ children: [/* @__PURE__ */ jsx("h3", {
2507
+ className: "sticky top-0 z-[1] bg-background px-2 py-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground",
2508
+ children: label
2509
+ }), /* @__PURE__ */ jsx("div", {
2510
+ className: "space-y-0.5",
2511
+ children
2512
+ })]
2682
2513
  });
2683
2514
  }
2684
- function SiteStyleRow({ expanded, onToggle, current, onPreview, disabled }) {
2685
- return /* @__PURE__ */ jsx(StyleRowFrame, {
2686
- label: "Site style",
2687
- currentLabel: current.name,
2688
- expanded,
2689
- onToggle,
2690
- children: /* @__PURE__ */ jsx("ul", {
2691
- role: "radiogroup",
2692
- "aria-label": "Site style",
2693
- className: "list-none space-y-1 p-0",
2694
- children: curatedSiteStyles.map((style) => /* @__PURE__ */ jsx(PickerOption, {
2695
- selected: style.id === current.id,
2696
- label: style.name,
2697
- description: style.description,
2698
- onSelect: () => onPreview(style.id),
2699
- disabled
2700
- }, style.id))
2701
- })
2515
+ function TypographyFontOptionRow({ option, selected, onSelect, disabled }) {
2516
+ return /* @__PURE__ */ jsxs("button", {
2517
+ type: "button",
2518
+ "aria-pressed": selected,
2519
+ "aria-label": option.label,
2520
+ className: cn("flex min-h-11 w-full items-center gap-3 rounded-md border border-transparent px-3 py-2 text-left transition disabled:cursor-not-allowed disabled:opacity-50", selected ? "border-primary/60 bg-primary/10" : "hover:border-border hover:bg-accent/30"),
2521
+ onClick: onSelect,
2522
+ disabled,
2523
+ children: [/* @__PURE__ */ jsx(FontPreviewText, {
2524
+ family: option.family,
2525
+ source: option.source,
2526
+ className: "min-w-0 flex-1 truncate text-base",
2527
+ children: option.label
2528
+ }), selected ? /* @__PURE__ */ jsx(Check, {
2529
+ className: "h-4 w-4 shrink-0 text-primary",
2530
+ "aria-hidden": "true"
2531
+ }) : null]
2702
2532
  });
2703
2533
  }
2704
- function PersonalityRow({ expanded, onToggle, siteStyle, currentId, currentLabel, onPreview, disabled }) {
2705
- return /* @__PURE__ */ jsx(StyleRowFrame, {
2706
- label: "Button style",
2707
- currentLabel,
2708
- expanded,
2709
- onToggle,
2710
- children: /* @__PURE__ */ jsx("ul", {
2711
- role: "radiogroup",
2712
- "aria-label": "Button style",
2713
- className: "list-none space-y-1 p-0",
2714
- children: useMemo(() => requireEntriesForCuratedChoices(buttonPersonalities, siteStyle.buttonPersonalityChoices), [siteStyle.buttonPersonalityChoices]).map((personality) => /* @__PURE__ */ jsx(PickerOption, {
2715
- selected: personality.id === currentId,
2716
- label: personality.name,
2717
- description: personality.description,
2718
- onSelect: () => onPreview(personality.id),
2719
- disabled
2720
- }, personality.id))
2721
- })
2534
+ function FontCatalogFeedback({ catalog }) {
2535
+ if ((catalog.google.kind === "loading" || catalog.google.kind === "idle") && catalog.google.value.length === 0) return /* @__PURE__ */ jsx("p", {
2536
+ className: "px-2 py-6 text-center text-sm text-muted-foreground",
2537
+ children: "Loading font catalog…"
2538
+ });
2539
+ const failureMessages = [catalog.site.kind === "failed" ? catalog.site.message : null, catalog.google.kind === "failed" ? catalog.google.message : null].filter((message) => message !== null);
2540
+ if (failureMessages.length > 0) return /* @__PURE__ */ jsx("div", {
2541
+ className: "mb-2 space-y-1",
2542
+ children: failureMessages.map((message) => /* @__PURE__ */ jsx("p", {
2543
+ className: "rounded-md bg-muted/60 px-3 py-2 text-xs text-muted-foreground",
2544
+ children: message
2545
+ }, message))
2722
2546
  });
2547
+ return null;
2723
2548
  }
2724
- function TypographyRow({ expanded, onToggle, apiClient, siteStyle, typography, currentPresetId, currentLabel, onPreviewPreset, onHeadingChange, onBodyChange, onScaleChange, onReset, disabled }) {
2725
- const [headingOpen, setHeadingOpen] = useState(true);
2726
- const [bodyOpen, setBodyOpen] = useState(false);
2727
- const options = useMemo(() => requireEntriesForCuratedChoices(typographyPresets, siteStyle.typographyPresetChoices), [siteStyle.typographyPresetChoices]);
2728
- const fontCatalog = useFontCatalog(apiClient, expanded);
2729
- const fontPolicyPreset = (currentPresetId ? findTypographyPreset(currentPresetId) : null) ?? options[0] ?? typographyPresets[0];
2730
- if (!fontPolicyPreset) return null;
2731
- return /* @__PURE__ */ jsxs(StyleRowFrame, {
2732
- label: "Typography",
2733
- currentLabel,
2734
- expanded,
2735
- onToggle,
2736
- children: [/* @__PURE__ */ jsxs("section", {
2737
- "aria-label": "Start from preset",
2738
- className: "space-y-2",
2739
- children: [/* @__PURE__ */ jsx("div", {
2740
- className: "text-xs font-medium text-muted-foreground",
2741
- children: "Start from preset"
2742
- }), /* @__PURE__ */ jsx("ul", {
2743
- role: "radiogroup",
2744
- "aria-label": "Typography preset",
2745
- className: "space-y-1",
2746
- children: options.map((preset) => /* @__PURE__ */ jsx(TypographyPresetOption, {
2747
- preset,
2748
- selected: preset.id === currentPresetId,
2749
- onSelect: () => onPreviewPreset(preset.id),
2750
- disabled
2751
- }, preset.id))
2752
- })]
2753
- }), /* @__PURE__ */ jsxs("div", {
2754
- className: "mt-3 space-y-2 border-t pt-3",
2755
- children: [
2756
- /* @__PURE__ */ jsxs(TypographyDisclosure, {
2549
+ function TypographyAdvancedTakeover({ typography, catalog, onBack, onHeadingChange, onBodyChange, onScaleChange, disabled }) {
2550
+ const headingOption = fontOptionForFamily(typography.headings.default.family, {
2551
+ googleFonts: catalog.google.value,
2552
+ siteFonts: catalog.site.value
2553
+ });
2554
+ const bodyOption = fontOptionForFamily(typography.body.family, {
2555
+ googleFonts: catalog.google.value,
2556
+ siteFonts: catalog.site.value
2557
+ });
2558
+ const headingWeightWarning = unsupportedWeightMessage(headingOption, typography.headings.default.weight, typography.headings.default.italic === true ? "italic" : "normal");
2559
+ const bodyWeightWarning = unsupportedWeightMessage(bodyOption, typography.body.weight, "normal");
2560
+ return /* @__PURE__ */ jsxs("section", {
2561
+ "aria-label": "Advanced typography",
2562
+ className: "flex min-h-0 flex-1 flex-col overflow-hidden bg-background",
2563
+ children: [
2564
+ /* @__PURE__ */ jsxs("header", {
2565
+ className: "flex items-center gap-2 border-b p-3",
2566
+ children: [/* @__PURE__ */ jsx(Button, {
2567
+ type: "button",
2568
+ size: "icon",
2569
+ variant: "ghost",
2570
+ className: "h-8 w-8 shrink-0",
2571
+ onClick: onBack,
2572
+ "aria-label": "Back to typography",
2573
+ children: /* @__PURE__ */ jsx(ChevronLeft, {
2574
+ className: "h-4 w-4",
2575
+ "aria-hidden": "true"
2576
+ })
2577
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", {
2578
+ className: "text-sm font-semibold",
2579
+ children: "Advanced typography"
2580
+ }), /* @__PURE__ */ jsx("p", {
2581
+ className: "text-xs text-muted-foreground",
2582
+ children: "Fine-tune the type system."
2583
+ })] })]
2584
+ }),
2585
+ /* @__PURE__ */ jsxs("div", {
2586
+ className: "min-h-0 flex-1 space-y-2 overflow-y-auto p-3",
2587
+ children: [/* @__PURE__ */ jsxs(TypographyDisclosure, {
2757
2588
  label: "Heading",
2758
- open: headingOpen,
2759
- onOpenChange: setHeadingOpen,
2589
+ defaultOpen: true,
2760
2590
  children: [
2761
- /* @__PURE__ */ jsx(TypographyFontSlotPicker, {
2762
- label: "Heading font",
2763
- slot: "heading",
2764
- preset: fontPolicyPreset,
2765
- catalog: fontCatalog,
2766
- value: typography.headings.default.family,
2767
- onChange: (family) => onHeadingChange({ family }),
2768
- disabled
2769
- }),
2770
2591
  /* @__PURE__ */ jsx(TypographySelectControl, {
2771
2592
  label: "Weight",
2772
2593
  value: typography.headings.default.weight,
@@ -2774,6 +2595,7 @@ function TypographyRow({ expanded, onToggle, apiClient, siteStyle, typography, c
2774
2595
  onChange: (weight) => onHeadingChange({ weight }),
2775
2596
  disabled
2776
2597
  }),
2598
+ headingWeightWarning ? /* @__PURE__ */ jsx(ContextualFontWarning, { message: headingWeightWarning }) : null,
2777
2599
  /* @__PURE__ */ jsx(TypographySelectControl, {
2778
2600
  label: "Line height",
2779
2601
  value: typography.headings.default.lineHeight,
@@ -2781,6 +2603,13 @@ function TypographyRow({ expanded, onToggle, apiClient, siteStyle, typography, c
2781
2603
  onChange: (lineHeight) => onHeadingChange({ lineHeight }),
2782
2604
  disabled
2783
2605
  }),
2606
+ /* @__PURE__ */ jsx(TypographySelectControl, {
2607
+ label: "Letter spacing",
2608
+ value: typography.headings.default.letterSpacing,
2609
+ options: typographyLetterSpacingOptions,
2610
+ onChange: (letterSpacing) => onHeadingChange({ letterSpacing }),
2611
+ disabled
2612
+ }),
2784
2613
  /* @__PURE__ */ jsx(TypographySelectControl, {
2785
2614
  label: "Scale",
2786
2615
  value: normalizeTypographyScaleForControl(typography.scale),
@@ -2788,27 +2617,31 @@ function TypographyRow({ expanded, onToggle, apiClient, siteStyle, typography, c
2788
2617
  onChange: onScaleChange,
2789
2618
  disabled
2790
2619
  }),
2620
+ /* @__PURE__ */ jsx(HeadingCaseControl, {
2621
+ value: typography.headings.default.case,
2622
+ onChange: (value) => onHeadingChange({ case: value }),
2623
+ disabled
2624
+ }),
2625
+ /* @__PURE__ */ jsxs("button", {
2626
+ type: "button",
2627
+ className: "flex w-full items-center justify-between rounded-md border px-3 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",
2628
+ "aria-pressed": typography.headings.default.italic === true,
2629
+ onClick: () => onHeadingChange({ italic: typography.headings.default.italic === true ? null : true }),
2630
+ disabled,
2631
+ children: ["Italic headings", /* @__PURE__ */ jsx("span", {
2632
+ className: "text-xs text-muted-foreground",
2633
+ children: typography.headings.default.italic === true ? "On" : "Default"
2634
+ })]
2635
+ }),
2791
2636
  /* @__PURE__ */ jsx(TypographyColorTokenControl, {
2792
- value: typography.headings.default.colorToken ?? "text",
2637
+ value: normalizeHeadingColorTokenForControl(typography.headings.default.colorToken),
2793
2638
  onChange: (colorToken) => onHeadingChange({ colorToken: colorToken === "text" ? null : colorToken }),
2794
2639
  disabled
2795
2640
  })
2796
2641
  ]
2797
- }),
2798
- /* @__PURE__ */ jsxs(TypographyDisclosure, {
2642
+ }), /* @__PURE__ */ jsxs(TypographyDisclosure, {
2799
2643
  label: "Body",
2800
- open: bodyOpen,
2801
- onOpenChange: setBodyOpen,
2802
2644
  children: [
2803
- /* @__PURE__ */ jsx(TypographyFontSlotPicker, {
2804
- label: "Body font",
2805
- slot: "body",
2806
- preset: fontPolicyPreset,
2807
- catalog: fontCatalog,
2808
- value: typography.body.family,
2809
- onChange: (family) => onBodyChange({ family }),
2810
- disabled
2811
- }),
2812
2645
  /* @__PURE__ */ jsx(TypographySelectControl, {
2813
2646
  label: "Size",
2814
2647
  value: typography.body.size,
@@ -2823,42 +2656,301 @@ function TypographyRow({ expanded, onToggle, apiClient, siteStyle, typography, c
2823
2656
  onChange: (weight) => onBodyChange({ weight }),
2824
2657
  disabled
2825
2658
  }),
2659
+ bodyWeightWarning ? /* @__PURE__ */ jsx(ContextualFontWarning, { message: bodyWeightWarning }) : null,
2826
2660
  /* @__PURE__ */ jsx(TypographySelectControl, {
2827
2661
  label: "Line height",
2828
2662
  value: typography.body.lineHeight,
2829
2663
  options: typographyLineHeightOptions,
2830
2664
  onChange: (lineHeight) => onBodyChange({ lineHeight }),
2831
2665
  disabled
2666
+ }),
2667
+ /* @__PURE__ */ jsx(TypographySelectControl, {
2668
+ label: "Letter spacing",
2669
+ value: typography.body.letterSpacing,
2670
+ options: typographyLetterSpacingOptions,
2671
+ onChange: (letterSpacing) => onBodyChange({ letterSpacing }),
2672
+ disabled
2832
2673
  })
2833
2674
  ]
2834
- }),
2835
- /* @__PURE__ */ jsx(Button, {
2836
- type: "button",
2837
- size: "sm",
2838
- variant: "outline",
2839
- onClick: onReset,
2840
- disabled,
2841
- children: "Reset typography"
2842
- })
2843
- ]
2844
- })]
2675
+ })]
2676
+ }),
2677
+ /* @__PURE__ */ jsx(SiteFontFaceStyles, { siteFonts: catalog.site.value })
2678
+ ]
2845
2679
  });
2846
2680
  }
2847
- const typographyWeightOptions = [
2848
- {
2849
- value: "light",
2850
- label: "Light"
2851
- },
2852
- {
2853
- value: "regular",
2854
- label: "Regular"
2855
- },
2856
- {
2857
- value: "medium",
2858
- label: "Medium"
2859
- },
2860
- {
2861
- value: "semibold",
2681
+ function TypographyDisclosure({ label, defaultOpen = false, children }) {
2682
+ const [open, setOpen] = useState(defaultOpen);
2683
+ return /* @__PURE__ */ jsx(Collapsible, {
2684
+ open,
2685
+ onOpenChange: setOpen,
2686
+ children: /* @__PURE__ */ jsxs("div", {
2687
+ className: "rounded-md border bg-muted/10",
2688
+ children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
2689
+ asChild: true,
2690
+ children: /* @__PURE__ */ jsxs("button", {
2691
+ type: "button",
2692
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm font-medium hover:bg-accent/30",
2693
+ "aria-expanded": open,
2694
+ children: [/* @__PURE__ */ jsx(ChevronRight, {
2695
+ className: cn("h-4 w-4 transition", open && "rotate-90"),
2696
+ "aria-hidden": "true"
2697
+ }), label]
2698
+ })
2699
+ }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
2700
+ className: "space-y-3 border-t px-3 py-3",
2701
+ children
2702
+ }) })]
2703
+ })
2704
+ });
2705
+ }
2706
+ function TypographySelectControl({ label, value, options, onChange, disabled }) {
2707
+ return /* @__PURE__ */ jsxs("label", {
2708
+ className: "block space-y-1",
2709
+ children: [/* @__PURE__ */ jsx("span", {
2710
+ className: "text-xs font-medium text-muted-foreground",
2711
+ children: label
2712
+ }), /* @__PURE__ */ jsxs(Select, {
2713
+ value,
2714
+ onValueChange: (next) => {
2715
+ const option = options.find((candidate) => candidate.value === next);
2716
+ if (option) onChange(option.value);
2717
+ },
2718
+ disabled,
2719
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
2720
+ size: "sm",
2721
+ className: "w-full",
2722
+ "aria-label": label,
2723
+ children: /* @__PURE__ */ jsx(SelectValue, {})
2724
+ }), /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
2725
+ value: option.value,
2726
+ children: option.label
2727
+ }, option.value)) })]
2728
+ })]
2729
+ });
2730
+ }
2731
+ function HeadingCaseControl({ value, onChange, disabled }) {
2732
+ const options = [
2733
+ {
2734
+ value: "default",
2735
+ label: "Default",
2736
+ resolved: null
2737
+ },
2738
+ {
2739
+ value: "normal",
2740
+ label: "Normal",
2741
+ resolved: "normal"
2742
+ },
2743
+ {
2744
+ value: "uppercase",
2745
+ label: "Uppercase",
2746
+ resolved: "uppercase"
2747
+ },
2748
+ {
2749
+ value: "smallCaps",
2750
+ label: "Small caps",
2751
+ resolved: "smallCaps"
2752
+ }
2753
+ ];
2754
+ return /* @__PURE__ */ jsxs("label", {
2755
+ className: "block space-y-1",
2756
+ children: [/* @__PURE__ */ jsx("span", {
2757
+ className: "text-xs font-medium text-muted-foreground",
2758
+ children: "Case"
2759
+ }), /* @__PURE__ */ jsxs(Select, {
2760
+ value: value ?? "default",
2761
+ onValueChange: (next) => {
2762
+ const option = options.find((candidate) => candidate.value === next);
2763
+ if (option) onChange(option.resolved);
2764
+ },
2765
+ disabled,
2766
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
2767
+ size: "sm",
2768
+ className: "w-full",
2769
+ "aria-label": "Heading case",
2770
+ children: /* @__PURE__ */ jsx(SelectValue, {})
2771
+ }), /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
2772
+ value: option.value,
2773
+ children: option.label
2774
+ }, option.value)) })]
2775
+ })]
2776
+ });
2777
+ }
2778
+ function TypographyColorTokenControl({ value, onChange, disabled }) {
2779
+ return /* @__PURE__ */ jsxs("section", {
2780
+ className: "space-y-2",
2781
+ "aria-label": "Default heading color",
2782
+ children: [/* @__PURE__ */ jsxs("div", { children: [
2783
+ /* @__PURE__ */ jsx("div", {
2784
+ className: "text-xs font-medium text-muted-foreground",
2785
+ children: "Default heading color"
2786
+ }),
2787
+ /* @__PURE__ */ jsx("p", {
2788
+ className: "text-[11px] text-muted-foreground",
2789
+ children: "Sections may override this for readability."
2790
+ }),
2791
+ value.kind === "existing" ? /* @__PURE__ */ jsxs("p", {
2792
+ className: "mt-1 text-[11px] text-muted-foreground",
2793
+ children: [
2794
+ "Current: ",
2795
+ formatColorTokenLabel(value.value),
2796
+ " (outside quick choices)"
2797
+ ]
2798
+ }) : null
2799
+ ] }), /* @__PURE__ */ jsx("div", {
2800
+ className: "grid grid-cols-2 gap-1",
2801
+ children: headingColorTokenOptions.map((option) => /* @__PURE__ */ jsx(Button, {
2802
+ type: "button",
2803
+ size: "sm",
2804
+ variant: value.kind === "curated" && value.value === option.value ? "secondary" : "outline",
2805
+ className: "justify-start text-xs",
2806
+ "aria-pressed": value.kind === "curated" && value.value === option.value,
2807
+ onClick: () => onChange(option.value),
2808
+ disabled,
2809
+ children: option.label
2810
+ }, option.value))
2811
+ })]
2812
+ });
2813
+ }
2814
+ function ContextualFontWarning({ message }) {
2815
+ return /* @__PURE__ */ jsx("p", {
2816
+ className: "rounded-md bg-warning/10 px-2 py-1.5 text-xs text-muted-foreground",
2817
+ children: message
2818
+ });
2819
+ }
2820
+ function FontPreviewText({ family, source, className, children }) {
2821
+ const elementRef = useRef(null);
2822
+ const [visible, setVisible] = useState(() => typeof globalThis.IntersectionObserver !== "function");
2823
+ useEffect(() => {
2824
+ if (visible || source !== "google") return;
2825
+ const element = elementRef.current;
2826
+ if (!element || typeof globalThis.IntersectionObserver !== "function") {
2827
+ setVisible(true);
2828
+ return;
2829
+ }
2830
+ const observer = new globalThis.IntersectionObserver((entries) => {
2831
+ if (!entries.some((entry) => entry.isIntersecting)) return;
2832
+ setVisible(true);
2833
+ observer.disconnect();
2834
+ }, { rootMargin: "160px 0px" });
2835
+ observer.observe(element);
2836
+ return () => observer.disconnect();
2837
+ }, [source, visible]);
2838
+ useEffect(() => {
2839
+ if (!visible || source !== "google") return;
2840
+ ensureGoogleFontPreviewLink(family);
2841
+ }, [
2842
+ family,
2843
+ source,
2844
+ visible
2845
+ ]);
2846
+ return /* @__PURE__ */ jsx("span", {
2847
+ ref: elementRef,
2848
+ className,
2849
+ style: source === "site" || source === "google" ? { fontFamily: quoteFontFamily(family) } : void 0,
2850
+ children
2851
+ });
2852
+ }
2853
+ function SiteFontFaceStyles({ siteFonts }) {
2854
+ const css = useMemo(() => {
2855
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
2856
+ if (!supabaseUrl || siteFonts.length === 0) return "";
2857
+ return generateFontFaceCss([...siteFonts], getSiteFontsStorageUrl(supabaseUrl));
2858
+ }, [siteFonts]);
2859
+ return css ? /* @__PURE__ */ jsx("style", {
2860
+ "data-typography-site-fonts": true,
2861
+ children: css
2862
+ }) : null;
2863
+ }
2864
+ function ensureGoogleFontPreviewLink(family) {
2865
+ const url = buildFontPreviewUrl(family);
2866
+ if (Array.from(document.head.querySelectorAll("link[data-font-preview]")).some((link) => link.href === url)) return;
2867
+ const link = document.createElement("link");
2868
+ link.rel = "stylesheet";
2869
+ link.href = url;
2870
+ link.dataset.fontPreview = family;
2871
+ document.head.appendChild(link);
2872
+ }
2873
+ function unsupportedWeightMessage(option, weight, style) {
2874
+ if (!option) return null;
2875
+ const numericWeight = semanticFontWeight(weight);
2876
+ if (option.source === "site") {
2877
+ const font = option.siteFont;
2878
+ if (font.isVariable && font.variableWeightMin !== null && font.variableWeightMax !== null) return numericWeight >= font.variableWeightMin && numericWeight <= font.variableWeightMax && font.files.some((file) => file.style === style) ? null : unsupportedFontStyleMessage(option.label, style);
2879
+ return font.files.some((file) => file.weight === numericWeight && file.style === style) ? null : unsupportedFontStyleMessage(option.label, style);
2880
+ }
2881
+ const variants = new Set(option.googleFont.variants);
2882
+ return (style === "italic" ? variants.has(numericWeight === 400 ? "italic" : `${numericWeight}italic`) : variants.has(String(numericWeight)) || numericWeight === 400 && variants.has("regular")) ? null : unsupportedFontStyleMessage(option.label, style);
2883
+ }
2884
+ function unsupportedFontStyleMessage(family, style) {
2885
+ return style === "italic" ? `${family} may not include italics at this weight.` : `${family} may not include this weight.`;
2886
+ }
2887
+ function semanticFontWeight(weight) {
2888
+ switch (weight) {
2889
+ case "light": return 300;
2890
+ case "regular": return 400;
2891
+ case "medium": return 500;
2892
+ case "semibold": return 600;
2893
+ case "bold": return 700;
2894
+ }
2895
+ }
2896
+ function normalizeTypographyScaleForControl(scale) {
2897
+ switch (scale) {
2898
+ case "compact":
2899
+ case "balanced":
2900
+ case "spacious": return scale;
2901
+ case "minorThird": return "compact";
2902
+ case "majorThird": return "balanced";
2903
+ case "perfectFourth": return "spacious";
2904
+ }
2905
+ }
2906
+ function normalizeHeadingColorTokenForControl(value) {
2907
+ switch (value) {
2908
+ case "primary":
2909
+ case "secondary":
2910
+ case "accent": return {
2911
+ kind: "curated",
2912
+ value
2913
+ };
2914
+ case "text":
2915
+ case void 0:
2916
+ case null: return {
2917
+ kind: "curated",
2918
+ value: "text"
2919
+ };
2920
+ default: return {
2921
+ kind: "existing",
2922
+ value
2923
+ };
2924
+ }
2925
+ }
2926
+ function formatColorTokenLabel(value) {
2927
+ const words = value.replace(/([a-z])([A-Z])/gu, "$1 $2").replace(/[-_]+/gu, " ").toLocaleLowerCase();
2928
+ return `${words.charAt(0).toLocaleUpperCase()}${words.slice(1)}`;
2929
+ }
2930
+ function familyMatches(left, right) {
2931
+ return left.toLocaleLowerCase() === right.toLocaleLowerCase();
2932
+ }
2933
+ function quoteFontFamily(family) {
2934
+ return `"${family.replace(/["\\]/gu, "")}", sans-serif`;
2935
+ }
2936
+ function errorMessage(error, fallback) {
2937
+ return error instanceof Error && error.message ? error.message : fallback;
2938
+ }
2939
+ const typographyWeightOptions = [
2940
+ {
2941
+ value: "light",
2942
+ label: "Light"
2943
+ },
2944
+ {
2945
+ value: "regular",
2946
+ label: "Regular"
2947
+ },
2948
+ {
2949
+ value: "medium",
2950
+ label: "Medium"
2951
+ },
2952
+ {
2953
+ value: "semibold",
2862
2954
  label: "Semibold"
2863
2955
  },
2864
2956
  {
@@ -2880,6 +2972,20 @@ const typographyLineHeightOptions = [
2880
2972
  label: "Loose"
2881
2973
  }
2882
2974
  ];
2975
+ const typographyLetterSpacingOptions = [
2976
+ {
2977
+ value: "tight",
2978
+ label: "Tight"
2979
+ },
2980
+ {
2981
+ value: "normal",
2982
+ label: "Normal"
2983
+ },
2984
+ {
2985
+ value: "loose",
2986
+ label: "Loose"
2987
+ }
2988
+ ];
2883
2989
  const bodySizeOptions = [
2884
2990
  {
2885
2991
  value: "md",
@@ -2911,7 +3017,7 @@ const typographyScaleOptions = [
2911
3017
  const headingColorTokenOptions = [
2912
3018
  {
2913
3019
  value: "text",
2914
- label: "Text"
3020
+ label: "Default text"
2915
3021
  },
2916
3022
  {
2917
3023
  value: "primary",
@@ -2926,400 +3032,770 @@ const headingColorTokenOptions = [
2926
3032
  label: "Accent"
2927
3033
  }
2928
3034
  ];
2929
- function TypographyDisclosure({ label, open, onOpenChange, children }) {
2930
- return /* @__PURE__ */ jsx(Collapsible, {
2931
- open,
2932
- onOpenChange,
2933
- children: /* @__PURE__ */ jsxs("div", {
2934
- className: "rounded-md border bg-muted/10",
2935
- children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
2936
- asChild: true,
2937
- children: /* @__PURE__ */ jsxs("button", {
2938
- type: "button",
2939
- className: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm font-medium hover:bg-accent/30",
2940
- "aria-expanded": open,
2941
- children: [/* @__PURE__ */ jsx(ChevronRight, {
2942
- className: cn("h-4 w-4 transition", open && "rotate-90"),
2943
- "aria-hidden": "true"
2944
- }), /* @__PURE__ */ jsx("span", { children: label })]
2945
- })
2946
- }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
2947
- className: "space-y-3 border-t px-3 py-3",
2948
- children
2949
- }) })]
2950
- })
3035
+ //#endregion
3036
+ //#region ../preview-next/src/client/preview/StyleConfigurator.tsx
3037
+ const PALETTE_BRAND_ROLES = [
3038
+ {
3039
+ key: "primary",
3040
+ label: "Primary"
3041
+ },
3042
+ {
3043
+ key: "primaryForeground",
3044
+ label: "Primary text"
3045
+ },
3046
+ {
3047
+ key: "secondary",
3048
+ label: "Secondary"
3049
+ },
3050
+ {
3051
+ key: "accent",
3052
+ label: "Accent"
3053
+ }
3054
+ ];
3055
+ const PALETTE_FEEDBACK_ROLES = [
3056
+ {
3057
+ key: "success",
3058
+ label: "Success"
3059
+ },
3060
+ {
3061
+ key: "warning",
3062
+ label: "Warning"
3063
+ },
3064
+ {
3065
+ key: "destructive",
3066
+ label: "Destructive"
3067
+ }
3068
+ ];
3069
+ const PALETTE_SURFACE_ROLES = [
3070
+ {
3071
+ key: "background",
3072
+ label: "Background"
3073
+ },
3074
+ {
3075
+ key: "surface",
3076
+ label: "Surface"
3077
+ },
3078
+ {
3079
+ key: "surfaceAlt",
3080
+ label: "Alternate surface"
3081
+ },
3082
+ {
3083
+ key: "muted",
3084
+ label: "Muted surface"
3085
+ },
3086
+ {
3087
+ key: "text",
3088
+ label: "Text"
3089
+ },
3090
+ {
3091
+ key: "border",
3092
+ label: "Border"
3093
+ }
3094
+ ];
3095
+ function StyleConfigurator({ siteId, apiClient, theme, selectionId, selectionVersion = null, selectionUpdatedAt, initialSelections, headerSelectionContext, footerSelectionContext, expandedRowRequest = null, onFocusedSurfaceChange, siteStyleAccess }) {
3096
+ const router = useRouter();
3097
+ const previewDesign = usePreviewDesignCoordinator();
3098
+ const siteChromeCustomize = useSiteChromeCustomizeOptional();
3099
+ const setSiteStyleStagedPreviewActive = siteChromeCustomize?.siteStyleHistory?.setStagedPreviewActive;
3100
+ const { applyFailed, applyStarted, applySucceeded, clearPreview, previewTheme } = previewDesign;
3101
+ const [isRefreshPending, startRefreshTransition] = useTransition();
3102
+ const baseSnapshot = useMemo(() => resolveStyleConfiguratorInitialSnapshot({
3103
+ theme,
3104
+ initialSelections: {
3105
+ ...initialSelections,
3106
+ selectionVersion: initialSelections.selectionVersion ?? selectionVersion ?? 0,
3107
+ selectionUpdatedAt: initialSelections.selectionUpdatedAt ?? selectionUpdatedAt ?? null
3108
+ },
3109
+ headerSelectionContext,
3110
+ footerSelectionContext
3111
+ }), [
3112
+ footerSelectionContext,
3113
+ headerSelectionContext,
3114
+ theme,
3115
+ initialSelections,
3116
+ selectionUpdatedAt,
3117
+ selectionVersion
3118
+ ]);
3119
+ const [state, setState] = useState(() => baseSnapshot ? createStyleConfiguratorState(baseSnapshot) : null);
3120
+ const [typographySurface, setTypographySurface] = useState(typographyOverviewSurface);
3121
+ const fontCatalog = useTypographyFontCatalog(apiClient, siteId, state?.expandedRow === "typography");
3122
+ const hasLoadedState = state !== null;
3123
+ const hasStagedSiteStylePreview = state ? isPreviewingChange(state) : false;
3124
+ const hasFocusedTypographySurface = state?.expandedRow === "typography" && typographySurface.kind !== "overview";
3125
+ const stateRef = useRef(state);
3126
+ const clearPreviewRef = useRef(clearPreview);
3127
+ const runnerRef = useRef(createOptimisticActionRunner());
3128
+ const appliedExpandedRowRequestIdRef = useRef(null);
3129
+ useEffect(() => {
3130
+ stateRef.current = state;
3131
+ }, [state]);
3132
+ useEffect(() => {
3133
+ if (state?.expandedRow !== "typography" && typographySurface.kind !== "overview") setTypographySurface(typographyOverviewSurface);
3134
+ }, [state?.expandedRow, typographySurface.kind]);
3135
+ useEffect(() => {
3136
+ onFocusedSurfaceChange?.(hasFocusedTypographySurface);
3137
+ return () => {
3138
+ if (hasFocusedTypographySurface) onFocusedSurfaceChange?.(false);
3139
+ };
3140
+ }, [hasFocusedTypographySurface, onFocusedSurfaceChange]);
3141
+ useEffect(() => {
3142
+ clearPreviewRef.current = clearPreview;
3143
+ }, [clearPreview]);
3144
+ useEffect(() => {
3145
+ if (!setSiteStyleStagedPreviewActive) return;
3146
+ setSiteStyleStagedPreviewActive(hasStagedSiteStylePreview);
3147
+ return () => {
3148
+ setSiteStyleStagedPreviewActive(false);
3149
+ };
3150
+ }, [hasStagedSiteStylePreview, setSiteStyleStagedPreviewActive]);
3151
+ useEffect(() => {
3152
+ if (!baseSnapshot) return;
3153
+ setState((current) => current ? syncStyleConfiguratorState(current, baseSnapshot) : createStyleConfiguratorState(baseSnapshot));
3154
+ }, [baseSnapshot]);
3155
+ useEffect(() => {
3156
+ if (!state || !baseSnapshot) {
3157
+ clearPreview("style-configurator");
3158
+ return;
3159
+ }
3160
+ if (!isPreviewingChange(state)) {
3161
+ clearPreview("style-configurator");
3162
+ return;
3163
+ }
3164
+ const siteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
3165
+ if (!siteStyle) {
3166
+ clearPreview("style-configurator");
3167
+ return;
3168
+ }
3169
+ previewTheme("style-configurator", planSiteStylePreviewChange({
3170
+ siteStyle,
3171
+ selection: state.previewed,
3172
+ baseTheme: theme,
3173
+ headerSelectionContext,
3174
+ footerSelectionContext
3175
+ }).themeOverlay, "site-style");
3176
+ }, [
3177
+ state,
3178
+ baseSnapshot,
3179
+ clearPreview,
3180
+ previewTheme,
3181
+ headerSelectionContext,
3182
+ footerSelectionContext,
3183
+ theme
3184
+ ]);
3185
+ useEffect(() => {
3186
+ const runner = runnerRef.current;
3187
+ return () => {
3188
+ runner.invalidate();
3189
+ clearPreviewRef.current("style-configurator");
3190
+ };
3191
+ }, []);
3192
+ useEffect(() => {
3193
+ if (!expandedRowRequest) return;
3194
+ if (appliedExpandedRowRequestIdRef.current === expandedRowRequest.id) return;
3195
+ setState((current) => {
3196
+ if (!current) return current;
3197
+ appliedExpandedRowRequestIdRef.current = expandedRowRequest.id;
3198
+ return openRow(current, expandedRowForPreviewDesignRow(expandedRowRequest.row));
3199
+ });
3200
+ }, [expandedRowRequest, hasLoadedState]);
3201
+ const handlePreviewSiteStyle = useCallback((siteStyleId) => {
3202
+ setState((current) => {
3203
+ if (!current) return current;
3204
+ const next = previewSiteStyle(current, siteStyleId);
3205
+ const siteStyle = getCuratedSiteStyle(siteStyleId);
3206
+ if (!siteStyle) return next;
3207
+ const result = planSiteStylePreviewChange({
3208
+ siteStyle,
3209
+ selection: next.previewed,
3210
+ baseTheme: theme,
3211
+ headerSelectionContext,
3212
+ footerSelectionContext
3213
+ });
3214
+ return applyResolvedSelections(next, {
3215
+ resolved: {
3216
+ ...result.resolvedSelections,
3217
+ typography: result.themeOverlay.typography
3218
+ },
3219
+ warnings: result.warnings
3220
+ });
3221
+ });
3222
+ }, [
3223
+ footerSelectionContext,
3224
+ headerSelectionContext,
3225
+ theme
3226
+ ]);
3227
+ const handlePreviewPersonality = useCallback((id) => {
3228
+ setState((current) => current ? previewButtonPersonality(current, id) : current);
3229
+ }, []);
3230
+ const handlePreviewHeaderLayout = useCallback((id) => {
3231
+ if (!id) return;
3232
+ setState((current) => current ? previewHeaderLayout(current, id) : current);
3233
+ }, []);
3234
+ const handlePreviewHeaderStyle = useCallback((id) => {
3235
+ if (!id) return;
3236
+ setState((current) => current ? previewHeaderStyle(current, id) : current);
3237
+ }, []);
3238
+ const handlePreviewFooterLook = useCallback((id) => {
3239
+ const plan = planSiteChromeLookSelection({
3240
+ surface: "footer",
3241
+ lookId: id
3242
+ });
3243
+ if (plan.kind !== "footer") return;
3244
+ setState((current) => current ? previewFooterLook(current, plan.next.footerLookId) : current);
3245
+ }, []);
3246
+ const handlePreviewFooterLayout = useCallback((id) => {
3247
+ if (!id) return;
3248
+ setState((current) => {
3249
+ if (!current) return current;
3250
+ const siteStyle = getCuratedSiteStyle(current.previewed.siteStyleId);
3251
+ if (!siteStyle) return current;
3252
+ const models = buildSiteChromeLookModels({
3253
+ state: current,
3254
+ siteStyle,
3255
+ headerSelectionContext,
3256
+ footerSelectionContext
3257
+ });
3258
+ const nextLookId = planSiteChromeFooterLayoutSelection({
3259
+ layoutId: id,
3260
+ currentLookId: current.previewed.footerLookId ?? models.footer.renderedLookId
3261
+ });
3262
+ return nextLookId ? previewFooterLook(current, nextLookId) : current;
3263
+ });
3264
+ }, [footerSelectionContext, headerSelectionContext]);
3265
+ const handlePreviewPaletteItem = useCallback((item) => {
3266
+ setState((current) => {
3267
+ if (!current) return current;
3268
+ if (item.kind === "curated") return previewCuratedPalette(current, item.valueKey);
3269
+ return previewSitePalette(current, {
3270
+ paletteId: item.paletteId,
3271
+ colors: item.colors,
3272
+ mode: item.mode,
3273
+ version: item.version
3274
+ });
3275
+ });
3276
+ }, []);
3277
+ const handleCreatePalette = useCallback(() => {
3278
+ setState((current) => current ? createPalette(current) : current);
3279
+ }, []);
3280
+ const handlePaletteLibraryLoaded = useCallback((response) => {
3281
+ setState((current) => current ? reconcilePaletteLibraryActive(current, response.active, response.palettes) : current);
3282
+ }, []);
3283
+ const patchTypography = useCallback((patch) => {
3284
+ setState((current) => {
3285
+ if (!current) return current;
3286
+ const siteStyle = getCuratedSiteStyle(current.previewed.siteStyleId);
3287
+ if (!siteStyle) return current;
3288
+ return applyTypographyDraftPatch(current, planSiteStylePreviewChange({
3289
+ siteStyle,
3290
+ selection: current.previewed,
3291
+ baseTheme: theme,
3292
+ headerSelectionContext,
3293
+ footerSelectionContext
3294
+ }).themeOverlay.typography, patch);
3295
+ });
3296
+ }, [
3297
+ footerSelectionContext,
3298
+ headerSelectionContext,
3299
+ theme
3300
+ ]);
3301
+ const handleHeadingTypographyChange = useCallback((patch) => {
3302
+ patchTypography({
3303
+ kind: "heading",
3304
+ patch
3305
+ });
3306
+ }, [patchTypography]);
3307
+ const handleBodyTypographyChange = useCallback((patch) => {
3308
+ patchTypography({
3309
+ kind: "body",
3310
+ patch
3311
+ });
3312
+ }, [patchTypography]);
3313
+ const handleTypographyScaleChange = useCallback((scale) => {
3314
+ patchTypography({
3315
+ kind: "scale",
3316
+ scale
3317
+ });
3318
+ }, [patchTypography]);
3319
+ const handleResetTypography = useCallback(() => {
3320
+ setState((current) => current ? resetTypographyToSaved(current) : current);
3321
+ }, []);
3322
+ const handleTokenOverride = useCallback((role, hex) => {
3323
+ setState((current) => {
3324
+ if (!current) return current;
3325
+ const palette = palettePreviewProjection(current.previewed.palette);
3326
+ if (palette.kind === "invalid") return current;
3327
+ if (!(role in palette.colors)) return current;
3328
+ const nextColors = paletteColorsSchema.safeParse({
3329
+ ...palette.colors,
3330
+ [role]: hex
3331
+ });
3332
+ return nextColors.success ? setPaletteColors(current, nextColors.data) : current;
3333
+ });
3334
+ }, []);
3335
+ const handleResetOverrides = useCallback(() => {
3336
+ setState((current) => {
3337
+ if (!current) return current;
3338
+ const palette = palettePreviewProjection(current.previewed.palette);
3339
+ const sourceValueKey = palette.kind === "curated" ? palette.valueKey : palette.kind === "curated-fork" ? palette.sourceValueKey : null;
3340
+ if (!sourceValueKey) return current;
3341
+ const curated = findCuratedPalette(sourceValueKey);
3342
+ return curated ? setPaletteColors(current, curated.colors) : current;
3343
+ });
3344
+ }, []);
3345
+ const handleToggleRow = useCallback((row) => {
3346
+ if (row === "typography") setTypographySurface(typographyOverviewSurface);
3347
+ setState((current) => current ? toggleRow(current, row) : current);
3348
+ }, []);
3349
+ const handleCancel = useCallback(() => {
3350
+ setTypographySurface(typographyOverviewSurface);
3351
+ setState((current) => current ? cancelPreview(current) : current);
3352
+ }, []);
3353
+ const handleDismissWarnings = useCallback(() => {
3354
+ setState((current) => current ? dismissWarnings(current) : current);
3355
+ }, []);
3356
+ const handleApply = useCallback(async (retry = false) => {
3357
+ const current = stateRef.current;
3358
+ let live = current;
3359
+ if (current && retry) try {
3360
+ const paletteAuthority = await apiClient({
3361
+ endpoint: "listSiteThemePalettes",
3362
+ params: { siteId }
3363
+ }).then(unwrapResponse);
3364
+ live = prepareStalePaletteRetry(reconcilePaletteLibraryActive(current, paletteAuthority.active, paletteAuthority.palettes), { sitePalettes: paletteAuthority.palettes });
3365
+ } catch (error) {
3366
+ reportPreviewDesignError("Failed to refresh palette authority", error);
3367
+ const message = PREVIEW_DESIGN_REFRESH_ERROR_MESSAGE;
3368
+ setState((latest) => latest ? markApplyFailed(latest, message, "stale") : latest);
3369
+ return;
3370
+ }
3371
+ if (!live || !isPreviewingChange(live)) return;
3372
+ if (!deriveStyleConfiguratorCapabilities(live).canApplyStaged) return;
3373
+ if (live !== current) {
3374
+ stateRef.current = live;
3375
+ setState(live);
3376
+ }
3377
+ const previewed = live.previewed;
3378
+ const siteStyle = getCuratedSiteStyle(previewed.siteStyleId);
3379
+ if (!siteStyle) return;
3380
+ const compiled = planSiteStylePreviewChange({
3381
+ siteStyle,
3382
+ selection: previewed,
3383
+ baseTheme: theme,
3384
+ headerSelectionContext,
3385
+ footerSelectionContext
3386
+ });
3387
+ await runnerRef.current.run({
3388
+ optimistic: () => {
3389
+ applyStarted("style-configurator");
3390
+ setState((current) => current ? markApplySaving(current) : current);
3391
+ },
3392
+ network: async () => {
3393
+ const body = buildApplySiteStyleSelectionBody({
3394
+ base: live.base,
3395
+ previewed: {
3396
+ ...previewed,
3397
+ buttonPersonalityId: compiled.resolvedSelections.buttonPersonalityId,
3398
+ headerLayoutId: compiled.resolvedSelections.headerLayoutId,
3399
+ headerStyleId: compiled.resolvedSelections.headerStyleId,
3400
+ headerLookId: compiled.resolvedSelections.headerLookId,
3401
+ footerLookId: compiled.resolvedSelections.footerLookId,
3402
+ typography: compiled.themeOverlay.typography
3403
+ },
3404
+ selectionId
3405
+ });
3406
+ if (!body.ok) throw new StyleApplyPlanError(paletteApplyPlanErrorMessage(body.error));
3407
+ return unwrapResponse(await apiClient({
3408
+ endpoint: "applySiteStyleSelection",
3409
+ params: { siteId },
3410
+ body: body.body
3411
+ }));
3412
+ },
3413
+ onSuccess: (saved) => {
3414
+ const persisted = selectionSnapshotFromApplySiteStyleSelectionResponse(saved, previewed);
3415
+ const historyEntry = styleConfiguratorHistoryEntryForPersistedSelection(live, persisted);
3416
+ setState((current) => current ? markApplySaved(current, persisted) : current);
3417
+ if (historyEntry) siteChromeCustomize?.siteStyleHistory?.recordAppliedSelection(historyEntry);
3418
+ applySucceeded("style-configurator");
3419
+ startRefreshTransition(() => {
3420
+ router.refresh();
3421
+ });
3422
+ },
3423
+ onError: (error) => {
3424
+ applyFailed("style-configurator");
3425
+ const applyError = classifyStyleApplyError(error);
3426
+ if (applyError.kind === "stale") startRefreshTransition(() => {
3427
+ router.refresh();
3428
+ });
3429
+ const message = applyError.message;
3430
+ setState((current) => current ? markApplyFailed(current, message, applyError.kind === "stale" ? "stale" : "retryable") : current);
3431
+ }
3432
+ });
3433
+ }, [
3434
+ apiClient,
3435
+ footerSelectionContext,
3436
+ headerSelectionContext,
3437
+ applyFailed,
3438
+ applyStarted,
3439
+ applySucceeded,
3440
+ router,
3441
+ selectionId,
3442
+ siteChromeCustomize?.siteStyleHistory,
3443
+ siteId,
3444
+ startRefreshTransition,
3445
+ theme
3446
+ ]);
3447
+ if (!state || !baseSnapshot) return /* @__PURE__ */ jsx("p", {
3448
+ className: "text-sm text-muted-foreground",
3449
+ children: "Loading design controls..."
2951
3450
  });
2952
- }
2953
- function TypographySelectControl({ label, value, options, onChange, disabled }) {
2954
- return /* @__PURE__ */ jsxs("label", {
2955
- className: "block space-y-1",
2956
- children: [/* @__PURE__ */ jsx("span", {
2957
- className: "text-xs font-medium text-muted-foreground",
2958
- children: label
2959
- }), /* @__PURE__ */ jsx("select", {
2960
- value,
2961
- onChange: (event) => onChange(event.currentTarget.value),
2962
- disabled,
2963
- 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",
2964
- children: options.map((option) => /* @__PURE__ */ jsx("option", {
2965
- value: option.value,
2966
- children: option.label
2967
- }, option.value))
2968
- })]
3451
+ const applyAllowed = canApplySiteStyle(siteStyleAccess);
3452
+ const stagedCapabilities = deriveStyleConfiguratorCapabilities(state);
3453
+ const previewingChange = isPreviewingChange(state);
3454
+ const saving = stagedCapabilities.isApplyingStaged || isRefreshPending;
3455
+ const aggregateMutationControlsDisabled = saving || !stagedCapabilities.canPreviewStaged;
3456
+ const currentSiteStyle = getCuratedSiteStyle(state.previewed.siteStyleId);
3457
+ if (!currentSiteStyle) return /* @__PURE__ */ jsx("p", {
3458
+ className: "text-sm text-muted-foreground",
3459
+ children: "This site uses a Site Style that cannot be configured here."
2969
3460
  });
2970
- }
2971
- function TypographyColorTokenControl({ value, onChange, disabled }) {
2972
- return /* @__PURE__ */ jsxs("section", {
2973
- className: "space-y-2",
2974
- "aria-label": "Default heading color",
2975
- children: [/* @__PURE__ */ jsxs("div", {
2976
- className: "flex items-center justify-between gap-2",
2977
- children: [/* @__PURE__ */ jsx("span", {
2978
- className: "text-xs font-medium text-muted-foreground",
2979
- children: "Default heading color"
2980
- }), /* @__PURE__ */ jsx("span", {
2981
- className: "text-[11px] text-muted-foreground",
2982
- children: "Sections may override"
2983
- })]
3461
+ const currentPersonality = findButtonPersonality(state.previewed.buttonPersonalityId);
3462
+ const currentPalette = palettePreviewProjection(state.previewed.palette);
3463
+ const paletteMutation = paletteMutationCapability(state);
3464
+ const paletteControlsDisabled = saving || !stagedCapabilities.canPreviewStaged || paletteMutation.kind !== "editable";
3465
+ const currentCuratedValueKey = currentPalette.kind === "curated" ? currentPalette.valueKey : currentPalette.kind === "curated-fork" ? currentPalette.sourceValueKey : null;
3466
+ const currentCuratedPalette = currentCuratedValueKey ? findCuratedPalette(currentCuratedValueKey) : null;
3467
+ const paletteIsCustom = currentPalette.kind !== "invalid" && (!currentCuratedPalette || currentPalette.mode !== currentCuratedPalette.mode || !paletteColorsEqual(currentPalette.colors, currentCuratedPalette.colors));
3468
+ const currentTypography = planSiteStylePreviewChange({
3469
+ siteStyle: currentSiteStyle,
3470
+ selection: state.previewed,
3471
+ baseTheme: theme,
3472
+ headerSelectionContext,
3473
+ footerSelectionContext
3474
+ }).themeOverlay.typography;
3475
+ const chromeLookModels = buildSiteChromeLookModels({
3476
+ state,
3477
+ siteStyle: currentSiteStyle,
3478
+ headerSelectionContext,
3479
+ footerSelectionContext
3480
+ });
3481
+ const currentHeaderLayout = getHeaderLayout(state.previewed.headerLayoutId);
3482
+ const currentHeaderStyle = getHeaderStyle(state.previewed.headerStyleId);
3483
+ const currentFooterLayout = getFooterLayout(chromeLookModels.footerLayout.renderedLayoutId);
3484
+ const currentFooterLook = state.previewed.footerLookId ? findFooterLook(state.previewed.footerLookId) : findFooterLook(chromeLookModels.footer.renderedLookId);
3485
+ const headerSurfaceState = siteChromeCustomize?.getSurfaceState("header") ?? { kind: "unavailable" };
3486
+ if (state.expandedRow === "typography" && typographySurface.kind !== "overview") return /* @__PURE__ */ jsxs("div", {
3487
+ className: "flex h-full min-h-0 flex-col",
3488
+ "data-style-configurator-focused-surface": true,
3489
+ children: [typographySurface.kind === "font-picker" ? /* @__PURE__ */ jsx(TypographyFontPickerTakeover, {
3490
+ slot: typographySurface.slot,
3491
+ value: typographySurface.slot === "heading" ? currentTypography.headings.default.family : currentTypography.body.family,
3492
+ catalog: fontCatalog,
3493
+ onBack: () => setTypographySurface(typographyOverviewSurface),
3494
+ onChange: (family) => typographySurface.slot === "heading" ? handleHeadingTypographyChange({ family }) : handleBodyTypographyChange({ family }),
3495
+ disabled: aggregateMutationControlsDisabled
3496
+ }) : /* @__PURE__ */ jsx(TypographyAdvancedTakeover, {
3497
+ typography: currentTypography,
3498
+ catalog: fontCatalog,
3499
+ onBack: () => setTypographySurface(typographyOverviewSurface),
3500
+ onHeadingChange: handleHeadingTypographyChange,
3501
+ onBodyChange: handleBodyTypographyChange,
3502
+ onScaleChange: handleTypographyScaleChange,
3503
+ disabled: aggregateMutationControlsDisabled
2984
3504
  }), /* @__PURE__ */ jsx("div", {
2985
- className: "grid grid-cols-2 gap-1",
2986
- children: headingColorTokenOptions.map((option) => /* @__PURE__ */ jsx("button", {
2987
- type: "button",
2988
- className: cn("rounded-md border px-2 py-1.5 text-left text-xs transition disabled:cursor-not-allowed disabled:opacity-50", value === option.value ? "border-primary bg-primary/10" : "border-border hover:bg-muted/60"),
2989
- "aria-pressed": value === option.value,
2990
- onClick: () => onChange(option.value),
2991
- disabled,
2992
- children: option.label
2993
- }, option.value))
3505
+ className: "shrink-0 px-3 pb-3",
3506
+ children: /* @__PURE__ */ jsx(StyleConfiguratorSaveControls, {
3507
+ previewingChange,
3508
+ applyAllowed,
3509
+ saving,
3510
+ canCancel: stagedCapabilities.canCancelStaged,
3511
+ canApply: stagedCapabilities.canApplyStaged,
3512
+ saveStatus: state.saveStatus,
3513
+ onCancel: handleCancel,
3514
+ onApply: () => void handleApply(),
3515
+ onRetry: () => void handleApply(true)
3516
+ })
2994
3517
  })]
2995
3518
  });
2996
- }
2997
- function normalizeTypographyScaleForControl(scale) {
2998
- switch (scale) {
2999
- case "compact":
3000
- case "balanced":
3001
- case "spacious": return scale;
3002
- case "minorThird": return "compact";
3003
- case "majorThird": return "balanced";
3004
- case "perfectFourth": return "spacious";
3005
- default: return "balanced";
3006
- }
3007
- }
3008
- function TypographyPresetOption({ preset, selected, onSelect, disabled }) {
3009
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
3010
- type: "button",
3011
- role: "radio",
3012
- "aria-checked": selected,
3013
- onClick: onSelect,
3014
- disabled,
3015
- className: cn("w-full rounded-md border px-3 py-2 text-left transition disabled:cursor-not-allowed disabled:opacity-50", selected ? "border-primary bg-primary/5" : "border-border hover:bg-muted/60"),
3016
- children: /* @__PURE__ */ jsxs("div", {
3017
- className: "flex items-start justify-between gap-3",
3018
- children: [/* @__PURE__ */ jsxs("div", {
3019
- className: "min-w-0",
3020
- children: [/* @__PURE__ */ jsx("div", {
3021
- className: "text-sm font-medium",
3022
- children: preset.name
3023
- }), /* @__PURE__ */ jsx("div", {
3024
- className: "text-xs text-muted-foreground",
3025
- children: preset.description
3026
- })]
3027
- }), /* @__PURE__ */ jsxs("div", {
3028
- className: "shrink-0 text-right",
3029
- children: [/* @__PURE__ */ jsx("div", {
3030
- className: "text-[11px] font-semibold uppercase tracking-wide",
3031
- children: preset.defaultFonts.heading.split(",")[0]
3032
- }), /* @__PURE__ */ jsx("div", {
3033
- className: "text-[11px] text-muted-foreground",
3034
- children: preset.defaultFonts.body.split(",")[0]
3035
- })]
3036
- })]
3037
- })
3038
- }) });
3039
- }
3040
- function TypographyFontSlotPicker({ label, slot, preset, catalog, value, onChange, disabled }) {
3041
- const [query, setQuery] = useState("");
3042
- const [manualFamilyDraft, setManualFamilyDraft] = useState(value);
3043
- const defaultFont = primaryFontFamily(preset.defaultFonts[slot]);
3044
- const selectedFamily = primaryFontFamily(value);
3045
- useEffect(() => {
3046
- setManualFamilyDraft(value);
3047
- }, [value]);
3048
- const commitManualFamilyDraft = useCallback(() => {
3049
- const normalized = normalizeFontFamilyOverride(manualFamilyDraft);
3050
- if (!normalized) {
3051
- setManualFamilyDraft(value);
3052
- return;
3053
- }
3054
- onChange(normalized);
3055
- }, [
3056
- manualFamilyDraft,
3057
- onChange,
3058
- value
3059
- ]);
3060
- const recordsByFamily = useMemo(() => indexFontsByFamily(catalog.fonts), [catalog.fonts]);
3061
- const selectedFit = evaluateTypographyFontSlot({
3062
- slot,
3063
- policy: preset.slotPolicies[slot],
3064
- capability: capabilityForFamily(selectedFamily, recordsByFamily)
3065
- });
3066
- const recommendedFamilies = preset.recommendedFonts[slot];
3067
- const recommendedOptions = useMemo(() => uniqueFontFamilies(recommendedFamilies), [recommendedFamilies]);
3068
- const browseOptions = useMemo(() => {
3069
- const normalizedQuery = query.trim().toLowerCase();
3070
- return [...normalizedQuery.length > 0 ? catalog.fonts.filter((font) => font.family.toLowerCase().includes(normalizedQuery)) : catalog.fonts].sort((a, b) => sortFontForPresetSlot(a, b, slot, preset, recordsByFamily)).slice(0, normalizedQuery.length > 0 ? MAX_FONT_RESULTS_WHEN_SEARCHING : MAX_FONT_RESULTS_WHEN_BROWSING);
3071
- }, [
3072
- catalog.fonts,
3073
- preset,
3074
- query,
3075
- recordsByFamily,
3076
- slot
3077
- ]);
3078
- return /* @__PURE__ */ jsxs("section", {
3079
- className: "space-y-2",
3080
- "aria-label": label,
3519
+ return /* @__PURE__ */ jsxs("div", {
3520
+ className: "space-y-3",
3081
3521
  children: [
3082
- /* @__PURE__ */ jsxs("div", {
3083
- className: "flex items-center justify-between gap-3",
3084
- children: [/* @__PURE__ */ jsxs("div", {
3085
- className: "min-w-0",
3086
- children: [/* @__PURE__ */ jsx("div", {
3087
- className: "text-xs font-medium text-muted-foreground",
3088
- children: label
3089
- }), /* @__PURE__ */ jsx("div", {
3090
- className: "truncate text-sm",
3091
- children: selectedFamily
3092
- })]
3093
- }), /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit: selectedFit })]
3522
+ state.warnings.length > 0 ? /* @__PURE__ */ jsx(WarningNotice, {
3523
+ warnings: state.warnings,
3524
+ onDismiss: handleDismissWarnings
3525
+ }) : null,
3526
+ /* @__PURE__ */ jsx(SiteStyleRow, {
3527
+ expanded: state.expandedRow === "style",
3528
+ onToggle: () => handleToggleRow("style"),
3529
+ current: currentSiteStyle,
3530
+ onPreview: handlePreviewSiteStyle,
3531
+ disabled: aggregateMutationControlsDisabled
3094
3532
  }),
3095
- /* @__PURE__ */ jsxs("div", {
3096
- className: "grid grid-cols-1 gap-1",
3097
- children: [/* @__PURE__ */ jsxs("button", {
3098
- type: "button",
3099
- className: cn("rounded-md border px-3 py-2 text-left text-sm transition disabled:cursor-not-allowed disabled:opacity-50", selectedFamily === defaultFont ? "border-foreground bg-accent/40" : "border-transparent hover:border-foreground/40 hover:bg-accent/20"),
3100
- onClick: () => onChange(preset.defaultFonts[slot]),
3101
- disabled,
3102
- children: [/* @__PURE__ */ jsx("span", {
3103
- className: "font-medium",
3104
- children: "Use recipe font"
3105
- }), /* @__PURE__ */ jsx("span", {
3106
- className: "ml-2 text-xs text-muted-foreground",
3107
- children: defaultFont
3108
- })]
3109
- }), recommendedOptions.map((family) => /* @__PURE__ */ jsx(FontFamilyOption, {
3110
- family,
3111
- selected: selectedFamily === family,
3112
- fit: evaluateTypographyFontSlot({
3113
- slot,
3114
- policy: preset.slotPolicies[slot],
3115
- capability: capabilityForFamily(family, recordsByFamily)
3116
- }),
3117
- onSelect: () => onChange(family),
3118
- disabled
3119
- }, family))]
3533
+ /* @__PURE__ */ jsx(SiteChromeLookRow, {
3534
+ label: "Header layout",
3535
+ ariaLabel: "Header layout",
3536
+ defaultDescription: "Choose the header structure.",
3537
+ expanded: state.expandedRow === "header-layout",
3538
+ onToggle: () => handleToggleRow("header-layout"),
3539
+ currentId: state.previewed.headerLayoutId,
3540
+ currentLabel: currentHeaderLayout?.label ?? state.previewed.headerLayoutId,
3541
+ scopeDescription: "Changes the header structure on every page.",
3542
+ options: chromeLookModels.headerLayout.options,
3543
+ diagnostics: [],
3544
+ onCycle: (direction) => {
3545
+ const next = cycleSiteChromeLookId(chromeLookModels.headerLayout.options, state.previewed.headerLayoutId, direction);
3546
+ if (next !== void 0) handlePreviewHeaderLayout(next);
3547
+ },
3548
+ onPreview: handlePreviewHeaderLayout,
3549
+ disabled: aggregateMutationControlsDisabled
3120
3550
  }),
3121
- /* @__PURE__ */ jsxs("div", {
3122
- className: "space-y-2 rounded-md border bg-muted/20 p-2",
3123
- children: [
3124
- /* @__PURE__ */ jsx("input", {
3125
- type: "search",
3126
- value: query,
3127
- "aria-label": `Search ${slot} fonts`,
3128
- placeholder: `Search ${slot} fonts`,
3129
- onChange: (event) => setQuery(event.currentTarget.value),
3130
- disabled: disabled || catalog.kind === "loading",
3131
- 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"
3132
- }),
3133
- catalog.kind === "loading" ? /* @__PURE__ */ jsx("p", {
3134
- className: "text-xs text-muted-foreground",
3135
- children: "Loading font catalog..."
3136
- }) : catalog.kind === "failed" ? /* @__PURE__ */ jsx("p", {
3137
- className: "text-xs text-muted-foreground",
3138
- children: catalog.message
3139
- }) : null,
3140
- browseOptions.length > 0 ? /* @__PURE__ */ jsx("div", {
3141
- className: "max-h-56 space-y-1 overflow-y-auto pr-1",
3142
- children: browseOptions.map((font) => /* @__PURE__ */ jsx(FontFamilyOption, {
3143
- family: font.family,
3144
- selected: selectedFamily === font.family,
3145
- fit: evaluateTypographyFontSlot({
3146
- slot,
3147
- policy: preset.slotPolicies[slot],
3148
- capability: capabilityFromFontRecord(font)
3149
- }),
3150
- onSelect: () => onChange(font.family),
3151
- disabled
3152
- }, font.family))
3153
- }) : catalog.kind === "loaded" ? /* @__PURE__ */ jsx("p", {
3154
- className: "text-xs text-muted-foreground",
3155
- children: "No matching Google Fonts."
3156
- }) : null
3157
- ]
3551
+ /* @__PURE__ */ jsx(SiteChromeLookRow, {
3552
+ label: "Header look",
3553
+ ariaLabel: "Header look",
3554
+ defaultDescription: "Choose the header visual style.",
3555
+ expanded: state.expandedRow === "header-style",
3556
+ onToggle: () => handleToggleRow("header-style"),
3557
+ currentId: state.previewed.headerStyleId,
3558
+ currentLabel: currentHeaderStyle?.label ?? state.previewed.headerStyleId,
3559
+ scopeDescription: "Changes the header surface and link style.",
3560
+ options: chromeLookModels.headerStyle.options,
3561
+ diagnostics: [],
3562
+ onCycle: (direction) => {
3563
+ const next = cycleSiteChromeLookId(chromeLookModels.headerStyle.options, state.previewed.headerStyleId, direction);
3564
+ if (next !== void 0) handlePreviewHeaderStyle(next);
3565
+ },
3566
+ onPreview: handlePreviewHeaderStyle,
3567
+ disabled: aggregateMutationControlsDisabled,
3568
+ extraControls: isHeaderMenuSurfaceState(headerSurfaceState) ? /* @__PURE__ */ jsx(HeaderMenuControls, {
3569
+ state: headerSurfaceState,
3570
+ disabled: saving,
3571
+ onMenuFontChange: siteChromeCustomize?.setHeaderMenuFont,
3572
+ onMenuFontSizeChange: siteChromeCustomize?.setHeaderMenuFontSize
3573
+ }) : null
3574
+ }),
3575
+ /* @__PURE__ */ jsx(SiteChromeLookRow, {
3576
+ label: "Footer layout",
3577
+ ariaLabel: "Footer layout",
3578
+ defaultDescription: "Choose the footer structure.",
3579
+ expanded: state.expandedRow === "footer-layout",
3580
+ onToggle: () => handleToggleRow("footer-layout"),
3581
+ currentId: chromeLookModels.footerLayout.renderedLayoutId,
3582
+ currentLabel: currentFooterLayout?.label ?? chromeLookModels.footerLayout.renderedLayoutId,
3583
+ scopeDescription: "Changes the footer structure on every page.",
3584
+ options: chromeLookModels.footerLayout.options,
3585
+ diagnostics: [],
3586
+ onCycle: (direction) => {
3587
+ const next = cycleSiteChromeLookId(chromeLookModels.footerLayout.options, chromeLookModels.footerLayout.renderedLayoutId, direction);
3588
+ if (next !== void 0) handlePreviewFooterLayout(next);
3589
+ },
3590
+ onPreview: handlePreviewFooterLayout,
3591
+ disabled: aggregateMutationControlsDisabled
3592
+ }),
3593
+ /* @__PURE__ */ jsx(SiteChromeLookRow, {
3594
+ label: "Footer look",
3595
+ ariaLabel: "Footer look",
3596
+ defaultDescription: "Use the footer look chosen for this site style.",
3597
+ expanded: state.expandedRow === "footer",
3598
+ onToggle: () => handleToggleRow("footer"),
3599
+ currentId: state.previewed.footerLookId,
3600
+ currentLabel: state.previewed.footerLookId ? currentFooterLook?.label ?? "Unknown footer Look" : `${currentFooterLook?.label ?? "Site style"} (default)`,
3601
+ scopeDescription: "Changes the footer on every page.",
3602
+ options: chromeLookModels.footer.options,
3603
+ diagnostics: chromeLookModels.footer.diagnostics,
3604
+ onCycle: (direction) => {
3605
+ const next = cycleSiteChromeLookId(chromeLookModels.footer.options, state.previewed.footerLookId, direction);
3606
+ if (next !== void 0) handlePreviewFooterLook(next);
3607
+ },
3608
+ onPreview: handlePreviewFooterLook,
3609
+ disabled: aggregateMutationControlsDisabled
3610
+ }),
3611
+ /* @__PURE__ */ jsx(PersonalityRow, {
3612
+ expanded: state.expandedRow === "buttons",
3613
+ onToggle: () => handleToggleRow("buttons"),
3614
+ siteStyle: currentSiteStyle,
3615
+ currentId: state.previewed.buttonPersonalityId,
3616
+ currentLabel: currentPersonality?.name ?? state.previewed.buttonPersonalityId,
3617
+ onPreview: handlePreviewPersonality,
3618
+ disabled: aggregateMutationControlsDisabled
3619
+ }),
3620
+ /* @__PURE__ */ jsx(TypographyRow, {
3621
+ expanded: state.expandedRow === "typography",
3622
+ onToggle: () => handleToggleRow("typography"),
3623
+ typography: currentTypography,
3624
+ catalog: fontCatalog,
3625
+ currentLabel: `${primaryFontFamily(currentTypography.headings.default.family)} / ${primaryFontFamily(currentTypography.body.family)}`,
3626
+ onChooseFont: (slot) => setTypographySurface({
3627
+ kind: "font-picker",
3628
+ slot
3629
+ }),
3630
+ onOpenAdvanced: () => setTypographySurface({ kind: "advanced" }),
3631
+ onReset: handleResetTypography,
3632
+ disabled: aggregateMutationControlsDisabled
3158
3633
  }),
3159
- /* @__PURE__ */ jsxs("label", {
3160
- className: "block space-y-1",
3161
- children: [/* @__PURE__ */ jsx("span", {
3162
- className: "text-xs text-muted-foreground",
3163
- children: "Brand or uploaded font name"
3164
- }), /* @__PURE__ */ jsx("input", {
3165
- type: "text",
3166
- value: manualFamilyDraft,
3167
- placeholder: "Enter any font family",
3168
- onChange: (event) => setManualFamilyDraft(event.currentTarget.value),
3169
- onBlur: commitManualFamilyDraft,
3170
- onKeyDown: (event) => {
3171
- if (event.key !== "Enter") return;
3172
- event.currentTarget.blur();
3173
- },
3174
- disabled,
3175
- 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"
3176
- })]
3634
+ /* @__PURE__ */ jsx(PaletteRow, {
3635
+ expanded: state.expandedRow === "palette",
3636
+ onToggle: () => handleToggleRow("palette"),
3637
+ apiClient,
3638
+ siteId,
3639
+ siteStyle: currentSiteStyle,
3640
+ palette: currentPalette,
3641
+ committedSelection: committedPaletteForSnapshot(state.base.palette)?.selection ?? null,
3642
+ onPreview: handlePreviewPaletteItem,
3643
+ onCreate: handleCreatePalette,
3644
+ onLibraryLoaded: handlePaletteLibraryLoaded,
3645
+ isCustom: paletteIsCustom,
3646
+ onTokenChange: handleTokenOverride,
3647
+ onReset: handleResetOverrides,
3648
+ mutationCapability: paletteMutation,
3649
+ disabled: saving || !applyAllowed,
3650
+ mutationDisabled: paletteControlsDisabled || !applyAllowed
3651
+ }),
3652
+ /* @__PURE__ */ jsx(StyleConfiguratorSaveControls, {
3653
+ previewingChange,
3654
+ applyAllowed,
3655
+ saving,
3656
+ canCancel: stagedCapabilities.canCancelStaged,
3657
+ canApply: stagedCapabilities.canApplyStaged,
3658
+ saveStatus: state.saveStatus,
3659
+ onCancel: handleCancel,
3660
+ onApply: () => void handleApply(),
3661
+ onRetry: () => void handleApply(true)
3177
3662
  })
3178
3663
  ]
3179
3664
  });
3180
3665
  }
3181
- function FontFamilyOption({ family, selected, fit, onSelect, disabled }) {
3182
- return /* @__PURE__ */ jsxs("button", {
3183
- type: "button",
3184
- className: cn("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", selected ? "border-foreground bg-accent/40" : "border-transparent bg-background hover:border-foreground/40 hover:bg-accent/20"),
3185
- onClick: onSelect,
3186
- disabled,
3187
- children: [/* @__PURE__ */ jsx("span", {
3188
- className: "truncate font-medium",
3189
- children: family
3190
- }), /* @__PURE__ */ jsx(TypographyFontFitBadge, { fit })]
3191
- });
3666
+ function StyleConfiguratorSaveControls({ previewingChange, applyAllowed, saving, canCancel, canApply, saveStatus, onCancel, onApply, onRetry }) {
3667
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
3668
+ previewingChange ? /* @__PURE__ */ jsxs("div", {
3669
+ className: "flex items-center gap-2 pt-2",
3670
+ children: [/* @__PURE__ */ jsx(Button, {
3671
+ type: "button",
3672
+ size: "sm",
3673
+ variant: "outline",
3674
+ onClick: onCancel,
3675
+ disabled: !canCancel || saving,
3676
+ children: "Cancel"
3677
+ }), applyAllowed ? /* @__PURE__ */ jsx(Button, {
3678
+ type: "button",
3679
+ size: "sm",
3680
+ onClick: onApply,
3681
+ disabled: !canApply || saving,
3682
+ children: saving ? "Applying…" : "Apply"
3683
+ }) : /* @__PURE__ */ jsx("span", {
3684
+ className: "text-xs text-muted-foreground",
3685
+ children: "Preview only — ask an admin to apply."
3686
+ })]
3687
+ }) : null,
3688
+ saveStatus.kind === "saved" ? /* @__PURE__ */ jsx("p", {
3689
+ className: "text-xs text-success",
3690
+ children: "Saved."
3691
+ }) : null,
3692
+ saveStatus.kind === "failed" ? /* @__PURE__ */ jsxs("div", {
3693
+ role: "status",
3694
+ className: "flex items-center gap-2 text-xs text-destructive",
3695
+ children: [/* @__PURE__ */ jsx("span", { children: saveStatus.message }), saveStatus.failureKind === "stale" && previewingChange && applyAllowed ? /* @__PURE__ */ jsx(Button, {
3696
+ type: "button",
3697
+ size: "sm",
3698
+ variant: "outline",
3699
+ onClick: onRetry,
3700
+ disabled: saving,
3701
+ children: "Retry"
3702
+ }) : null]
3703
+ }) : null
3704
+ ] });
3192
3705
  }
3193
- function TypographyFontFitBadge({ fit }) {
3194
- return /* @__PURE__ */ jsx("span", {
3195
- className: cn("shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium", fit.kind === "recommended" && "bg-emerald-100 text-emerald-800", fit.kind === "compatible" && "bg-sky-100 text-sky-800", fit.kind === "limited" && "bg-amber-100 text-amber-900", fit.kind === "risky" && "bg-rose-100 text-rose-800", fit.kind === "unknown" && "bg-muted text-muted-foreground"),
3196
- title: fit.reason,
3197
- children: typographyFontFitLabel(fit.kind)
3706
+ function StyleRowFrame({ label, currentLabel, swatch, expanded, onToggle, children }) {
3707
+ return /* @__PURE__ */ jsx(Collapsible, {
3708
+ open: expanded,
3709
+ onOpenChange: onToggle,
3710
+ children: /* @__PURE__ */ jsxs("div", {
3711
+ className: "rounded-md border bg-background",
3712
+ children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
3713
+ asChild: true,
3714
+ children: /* @__PURE__ */ jsxs("button", {
3715
+ type: "button",
3716
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-accent/30",
3717
+ "aria-expanded": expanded,
3718
+ children: [
3719
+ /* @__PURE__ */ jsx(ChevronRight, { className: cn("h-4 w-4 transition", expanded && "rotate-90") }),
3720
+ /* @__PURE__ */ jsxs("div", {
3721
+ className: "flex-1 min-w-0",
3722
+ children: [/* @__PURE__ */ jsx("div", {
3723
+ className: "text-xs uppercase tracking-wide text-muted-foreground",
3724
+ children: label
3725
+ }), /* @__PURE__ */ jsx("div", {
3726
+ className: "truncate text-sm font-medium",
3727
+ children: currentLabel
3728
+ })]
3729
+ }),
3730
+ swatch ? /* @__PURE__ */ jsx("span", {
3731
+ className: "h-5 w-5 shrink-0 rounded-full border border-black/10",
3732
+ style: { backgroundColor: swatch },
3733
+ "aria-hidden": "true"
3734
+ }) : null
3735
+ ]
3736
+ })
3737
+ }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
3738
+ className: "border-t px-3 py-3",
3739
+ children
3740
+ }) })]
3741
+ })
3198
3742
  });
3199
3743
  }
3200
- function useFontCatalog(apiClient, enabled) {
3201
- const [catalog, setCatalog] = useState({
3202
- kind: "idle",
3203
- fonts: []
3744
+ function SiteStyleRow({ expanded, onToggle, current, onPreview, disabled }) {
3745
+ return /* @__PURE__ */ jsx(StyleRowFrame, {
3746
+ label: "Site style",
3747
+ currentLabel: current.name,
3748
+ expanded,
3749
+ onToggle,
3750
+ children: /* @__PURE__ */ jsx("ul", {
3751
+ role: "radiogroup",
3752
+ "aria-label": "Site style",
3753
+ className: "list-none space-y-1 p-0",
3754
+ children: curatedSiteStyles.map((style) => /* @__PURE__ */ jsx(PickerOption, {
3755
+ selected: style.id === current.id,
3756
+ label: style.name,
3757
+ description: style.description,
3758
+ onSelect: () => onPreview(style.id),
3759
+ disabled
3760
+ }, style.id))
3761
+ })
3204
3762
  });
3205
- const requestedClientRef = useRef(null);
3206
- useEffect(() => {
3207
- if (!enabled) return;
3208
- if (requestedClientRef.current === apiClient) return;
3209
- requestedClientRef.current = apiClient;
3210
- let cancelled = false;
3211
- setCatalog({
3212
- kind: "loading",
3213
- fonts: []
3214
- });
3215
- async function loadFonts() {
3216
- try {
3217
- const fonts = unwrapResponse(await apiClient({ endpoint: "listFonts" }));
3218
- if (!cancelled) setCatalog({
3219
- kind: "loaded",
3220
- fonts: Array.isArray(fonts) ? fonts : []
3221
- });
3222
- } catch (error) {
3223
- if (!cancelled) setCatalog({
3224
- kind: "failed",
3225
- fonts: [],
3226
- message: error instanceof Error ? error.message : "Unable to load the font catalog."
3227
- });
3228
- }
3229
- }
3230
- loadFonts();
3231
- return () => {
3232
- cancelled = true;
3233
- };
3234
- }, [apiClient, enabled]);
3235
- return catalog;
3236
- }
3237
- function indexFontsByFamily(fonts) {
3238
- return new Map(fonts.map((font) => [font.family.toLowerCase(), font]));
3239
- }
3240
- function capabilityForFamily(family, recordsByFamily) {
3241
- const record = recordsByFamily.get(family.toLowerCase());
3242
- if (record) return capabilityFromFontRecord(record);
3243
- return {
3244
- source: "unknown",
3245
- family,
3246
- category: "unknown",
3247
- variants: []
3248
- };
3249
- }
3250
- function capabilityFromFontRecord(font) {
3251
- return {
3252
- source: "google",
3253
- family: font.family,
3254
- category: normalizeGoogleFontCategory(font.category),
3255
- variants: font.variants
3256
- };
3257
- }
3258
- function normalizeGoogleFontCategory(category) {
3259
- switch (category) {
3260
- case "serif":
3261
- case "sans-serif":
3262
- case "display":
3263
- case "handwriting":
3264
- case "monospace": return category;
3265
- default: return "unknown";
3266
- }
3267
- }
3268
- function sortFontForPresetSlot(a, b, slot, preset, recordsByFamily) {
3269
- const aFit = evaluateTypographyFontSlot({
3270
- slot,
3271
- policy: preset.slotPolicies[slot],
3272
- capability: capabilityForFamily(a.family, recordsByFamily)
3273
- });
3274
- const bFit = evaluateTypographyFontSlot({
3275
- slot,
3276
- policy: preset.slotPolicies[slot],
3277
- capability: capabilityForFamily(b.family, recordsByFamily)
3278
- });
3279
- const fitDelta = typographyFontFitRank(aFit.kind) - typographyFontFitRank(bFit.kind);
3280
- if (fitDelta !== 0) return fitDelta;
3281
- const popularityDelta = (a.popularity ?? Infinity) - (b.popularity ?? Infinity);
3282
- if (popularityDelta !== 0) return popularityDelta;
3283
- return a.family.localeCompare(b.family);
3284
- }
3285
- function typographyFontFitRank(kind) {
3286
- switch (kind) {
3287
- case "recommended": return 0;
3288
- case "compatible": return 1;
3289
- case "limited": return 2;
3290
- case "risky": return 3;
3291
- case "unknown": return 4;
3292
- default: return kind;
3293
- }
3294
- }
3295
- function typographyFontFitLabel(kind) {
3296
- switch (kind) {
3297
- case "recommended": return "Recommended";
3298
- case "compatible": return "Compatible";
3299
- case "limited": return "Limited";
3300
- case "risky": return "Risky";
3301
- case "unknown": return "Unknown";
3302
- default: return kind;
3303
- }
3304
- }
3305
- function primaryFontFamily(cssFamily) {
3306
- return cssFamily.split(",")[0]?.trim().replace(/^["']|["']$/gu, "") || cssFamily;
3307
3763
  }
3308
- function uniqueFontFamilies(families) {
3309
- const seen = /* @__PURE__ */ new Set();
3310
- const result = [];
3311
- for (const family of families) {
3312
- const normalized = primaryFontFamily(family);
3313
- const key = normalized.toLowerCase();
3314
- if (seen.has(key)) continue;
3315
- seen.add(key);
3316
- result.push(normalized);
3317
- }
3318
- return result;
3764
+ function PersonalityRow({ expanded, onToggle, siteStyle, currentId, currentLabel, onPreview, disabled }) {
3765
+ return /* @__PURE__ */ jsx(StyleRowFrame, {
3766
+ label: "Button style",
3767
+ currentLabel,
3768
+ expanded,
3769
+ onToggle,
3770
+ children: /* @__PURE__ */ jsx("ul", {
3771
+ role: "radiogroup",
3772
+ "aria-label": "Button style",
3773
+ className: "list-none space-y-1 p-0",
3774
+ children: useMemo(() => requireEntriesForCuratedChoices(buttonPersonalities, siteStyle.buttonPersonalityChoices), [siteStyle.buttonPersonalityChoices]).map((personality) => /* @__PURE__ */ jsx(PickerOption, {
3775
+ selected: personality.id === currentId,
3776
+ label: personality.name,
3777
+ description: personality.description,
3778
+ onSelect: () => onPreview(personality.id),
3779
+ disabled
3780
+ }, personality.id))
3781
+ })
3782
+ });
3319
3783
  }
3320
- function normalizeFontFamilyOverride(value) {
3321
- const trimmed = value.trim();
3322
- return trimmed.length > 0 ? trimmed : null;
3784
+ function TypographyRow({ expanded, onToggle, typography, catalog, currentLabel, onChooseFont, onOpenAdvanced, onReset, disabled }) {
3785
+ return /* @__PURE__ */ jsx(StyleRowFrame, {
3786
+ label: "Typography",
3787
+ currentLabel,
3788
+ expanded,
3789
+ onToggle,
3790
+ children: /* @__PURE__ */ jsx(TypographyOverviewContent, {
3791
+ typography,
3792
+ catalog,
3793
+ onChooseFont,
3794
+ onOpenAdvanced,
3795
+ onReset,
3796
+ disabled
3797
+ })
3798
+ });
3323
3799
  }
3324
3800
  function SiteChromeLookRow({ label, ariaLabel, defaultDescription, catalog, options = [{
3325
3801
  id: null,
@@ -3417,93 +3893,255 @@ function HeaderMenuControls({ state, disabled, onMenuFontChange, onMenuFontSizeC
3417
3893
  })]
3418
3894
  });
3419
3895
  }
3420
- function PaletteRow({ expanded, onToggle, siteStyle, currentId, currentLabel, currentSwatch, onPreview, mutationCapability, disabled }) {
3421
- const options = useMemo(() => {
3422
- const recommended = requireEntriesForCuratedChoices(curatedPalettes, siteStyle.paletteRecommendations);
3423
- if (recommended.some((variant) => variant.id === currentId)) return recommended;
3424
- const current = currentId ? findCuratedPalette(currentId) : null;
3425
- return current ? [current, ...recommended] : recommended;
3426
- }, [currentId, siteStyle.paletteRecommendations]);
3896
+ function PaletteRow({ expanded, onToggle, apiClient, siteId, siteStyle, palette, committedSelection, onPreview, onCreate, onLibraryLoaded, isCustom, onTokenChange, onReset, mutationCapability, disabled, mutationDisabled }) {
3897
+ const [library, setLibrary] = useState({
3898
+ kind: "idle",
3899
+ palettes: []
3900
+ });
3901
+ const optionRefs = useRef([]);
3902
+ const activeSelection = selectionForPaletteSnapshot(palette) ?? committedSelection;
3903
+ const libraryModel = useMemo(() => buildPaletteLibraryReadModel({
3904
+ active: activeSelection ? {
3905
+ kind: "available",
3906
+ selection: activeSelection
3907
+ } : { kind: "invalid" },
3908
+ sitePalettes: library.palettes,
3909
+ curatedPalettes,
3910
+ recommendations: siteStyle.paletteRecommendations.map((recommendation) => recommendation.id)
3911
+ }), [
3912
+ activeSelection,
3913
+ library.palettes,
3914
+ siteStyle.paletteRecommendations
3915
+ ]);
3916
+ useEffect(() => {
3917
+ if (!expanded || library.kind !== "idle") return;
3918
+ setLibrary((current) => ({
3919
+ kind: "loading",
3920
+ palettes: current.palettes
3921
+ }));
3922
+ }, [expanded, library.kind]);
3923
+ useEffect(() => {
3924
+ if (library.kind !== "loading") return;
3925
+ let cancelled = false;
3926
+ apiClient({
3927
+ endpoint: "listSiteThemePalettes",
3928
+ params: { siteId }
3929
+ }).then(unwrapResponse).then((response) => {
3930
+ if (cancelled) return;
3931
+ setLibrary({
3932
+ kind: "loaded",
3933
+ palettes: response.palettes
3934
+ });
3935
+ onLibraryLoaded(response);
3936
+ }).catch((error) => {
3937
+ if (cancelled) return;
3938
+ reportPreviewDesignError("Failed to load site palettes", error);
3939
+ setLibrary((current) => ({
3940
+ kind: "failed",
3941
+ palettes: current.palettes,
3942
+ message: PREVIEW_DESIGN_LOAD_ERROR_MESSAGE
3943
+ }));
3944
+ });
3945
+ return () => {
3946
+ cancelled = true;
3947
+ };
3948
+ }, [
3949
+ apiClient,
3950
+ library.kind,
3951
+ onLibraryLoaded,
3952
+ siteId
3953
+ ]);
3954
+ const items = libraryModel?.ok ? [
3955
+ ...libraryModel.model.yours,
3956
+ ...libraryModel.model.suggested,
3957
+ ...libraryModel.model.more
3958
+ ] : [];
3959
+ const currentLabel = palette.kind === "new-site" ? "New palette" : "Selected palette";
3960
+ const onOptionKeyDown = (event, index) => {
3961
+ if (items.length === 0) return;
3962
+ const direction = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" || event.key === "ArrowLeft" ? -1 : 0;
3963
+ if (direction === 0) return;
3964
+ event.preventDefault();
3965
+ const nextIndex = (index + direction + items.length) % items.length;
3966
+ const next = items[nextIndex];
3967
+ if (!next) return;
3968
+ onPreview(next);
3969
+ optionRefs.current[nextIndex]?.focus();
3970
+ };
3427
3971
  return /* @__PURE__ */ jsxs(StyleRowFrame, {
3428
3972
  label: "Palette",
3429
3973
  currentLabel,
3430
- swatch: currentSwatch,
3974
+ swatch: palette.kind === "invalid" ? null : palette.colors.primary,
3431
3975
  expanded,
3432
3976
  onToggle,
3433
3977
  children: [
3434
- /* @__PURE__ */ jsx("p", {
3435
- className: "mb-3 text-xs text-muted-foreground",
3436
- children: "These palette bases are recommended for the current Site Style. Choose a base here, then edit exact colours in Palette colours."
3437
- }),
3438
3978
  mutationCapability.kind === "blocked" && mutationCapability.reason === "invalid-palette" ? /* @__PURE__ */ jsx("div", {
3439
3979
  role: "status",
3440
3980
  className: "mb-3 flex items-center justify-between gap-3 rounded-md border border-muted px-3 py-2 text-xs text-muted-foreground",
3441
3981
  children: /* @__PURE__ */ jsx("span", { children: mutationCapability.message })
3442
3982
  }) : null,
3443
- /* @__PURE__ */ jsx("ul", {
3983
+ library.kind === "loading" ? /* @__PURE__ */ jsx("p", {
3984
+ className: "text-xs text-muted-foreground",
3985
+ children: "Loading palettes..."
3986
+ }) : null,
3987
+ library.kind === "failed" ? /* @__PURE__ */ jsxs("div", {
3988
+ role: "status",
3989
+ className: "space-y-2 text-xs text-muted-foreground",
3990
+ children: [/* @__PURE__ */ jsx("p", { children: library.message }), /* @__PURE__ */ jsx(Button, {
3991
+ type: "button",
3992
+ size: "sm",
3993
+ variant: "outline",
3994
+ onClick: () => setLibrary({
3995
+ kind: "idle",
3996
+ palettes: library.palettes
3997
+ }),
3998
+ children: "Retry loading palettes"
3999
+ })]
4000
+ }) : null,
4001
+ libraryModel.ok ? /* @__PURE__ */ jsxs("div", {
3444
4002
  role: "radiogroup",
3445
- "aria-label": "Palette",
3446
- className: "list-none space-y-1 p-0",
3447
- children: options.map((variant) => {
3448
- const primaryHex = variant.colors.primary;
3449
- return /* @__PURE__ */ jsx(PickerOption, {
3450
- selected: variant.id === currentId,
3451
- label: variant.name,
3452
- description: variant.description,
3453
- swatch: primaryHex,
3454
- onSelect: () => onPreview(variant.id),
4003
+ "aria-label": "Palette library",
4004
+ className: "space-y-3",
4005
+ children: [
4006
+ /* @__PURE__ */ jsx(PaletteLibraryGroup, {
4007
+ label: "Your palettes",
4008
+ items: libraryModel.model.yours,
4009
+ offset: 0,
4010
+ optionRefs,
4011
+ onPreview,
4012
+ onOptionKeyDown,
3455
4013
  disabled
3456
- }, variant.id);
3457
- })
3458
- })
4014
+ }),
4015
+ /* @__PURE__ */ jsx(PaletteLibraryGroup, {
4016
+ label: "Suggested for this style",
4017
+ items: libraryModel.model.suggested,
4018
+ offset: libraryModel.model.yours.length,
4019
+ optionRefs,
4020
+ onPreview,
4021
+ onOptionKeyDown,
4022
+ disabled
4023
+ }),
4024
+ /* @__PURE__ */ jsx(PaletteLibraryGroup, {
4025
+ label: "More palettes",
4026
+ items: libraryModel.model.more,
4027
+ offset: libraryModel.model.yours.length + libraryModel.model.suggested.length,
4028
+ optionRefs,
4029
+ onPreview,
4030
+ onOptionKeyDown,
4031
+ disabled
4032
+ })
4033
+ ]
4034
+ }) : /* @__PURE__ */ jsx("p", {
4035
+ role: "status",
4036
+ className: "text-xs text-muted-foreground",
4037
+ children: "Palette details are unavailable."
4038
+ }),
4039
+ /* @__PURE__ */ jsxs(Button, {
4040
+ type: "button",
4041
+ size: "sm",
4042
+ variant: "outline",
4043
+ className: "mt-3",
4044
+ onClick: onCreate,
4045
+ disabled: mutationDisabled,
4046
+ children: [/* @__PURE__ */ jsx(Plus, {
4047
+ "aria-hidden": "true",
4048
+ className: "size-3.5"
4049
+ }), "Create palette"]
4050
+ }),
4051
+ palette.kind !== "invalid" ? /* @__PURE__ */ jsx(PaletteTokenControls, {
4052
+ palette: palette.colors,
4053
+ isCustom,
4054
+ onTokenChange,
4055
+ onReset,
4056
+ disabled: mutationDisabled
4057
+ }) : null
3459
4058
  ]
3460
4059
  });
3461
4060
  }
3462
- function ColoursRow({ expanded, onToggle, palette, isCustom, onTokenChange, onReset, mutationCapability, disabled }) {
3463
- const mergedPalette = useMemo(() => paletteColorEntries(palette), [palette]);
3464
- return /* @__PURE__ */ jsx(StyleRowFrame, {
3465
- label: "Palette colours",
3466
- currentLabel: isCustom ? "Custom palette colours" : "Using selected palette",
3467
- expanded,
3468
- onToggle,
3469
- children: /* @__PURE__ */ jsxs("div", {
3470
- className: "space-y-3",
3471
- children: [
3472
- /* @__PURE__ */ jsx("p", {
3473
- className: "text-xs text-muted-foreground",
3474
- children: "Customise the palette tokens used across the site, including backgrounds, surfaces, text and borders."
3475
- }),
3476
- mutationCapability.kind === "blocked" ? /* @__PURE__ */ jsx("div", {
3477
- role: "status",
3478
- className: "flex items-center justify-between gap-3 rounded-md border border-muted px-3 py-2 text-xs text-muted-foreground",
3479
- children: /* @__PURE__ */ jsx("span", { children: mutationCapability.message })
3480
- }) : null,
3481
- /* @__PURE__ */ jsx("fieldset", {
3482
- disabled,
3483
- className: "min-w-0 border-0 p-0",
3484
- children: /* @__PURE__ */ jsx(PaletteTokenEditor, {
3485
- palette: mergedPalette,
3486
- onChange: onTokenChange,
3487
- brandRoles: PALETTE_BRAND_ROLES,
3488
- feedbackRoles: PALETTE_FEEDBACK_ROLES,
3489
- surfaceRoles: PALETTE_SURFACE_ROLES,
3490
- showContrastChecks: false,
3491
- disabled
3492
- })
3493
- }),
3494
- isCustom ? /* @__PURE__ */ jsx("div", {
3495
- className: "flex justify-end",
3496
- children: /* @__PURE__ */ jsx(Button, {
3497
- type: "button",
3498
- variant: "outline",
3499
- size: "sm",
3500
- onClick: onReset,
3501
- disabled,
3502
- children: "Reset to variant"
4061
+ function PaletteLibraryGroup({ label, items, offset, optionRefs, onPreview, onOptionKeyDown, disabled }) {
4062
+ if (items.length === 0) return null;
4063
+ return /* @__PURE__ */ jsxs("section", {
4064
+ "aria-label": label,
4065
+ children: [/* @__PURE__ */ jsx("h3", {
4066
+ className: "mb-1.5 text-xs font-medium text-muted-foreground",
4067
+ children: label
4068
+ }), /* @__PURE__ */ jsx("div", {
4069
+ className: "grid grid-cols-4 gap-2",
4070
+ children: items.map((item, index) => /* @__PURE__ */ jsxs("button", {
4071
+ ref: (element) => {
4072
+ optionRefs.current[offset + index] = element;
4073
+ },
4074
+ type: "button",
4075
+ role: "radio",
4076
+ "aria-label": item.accessibleLabel,
4077
+ "aria-checked": item.selected,
4078
+ disabled,
4079
+ onClick: () => onPreview(item),
4080
+ onKeyDown: (event) => onOptionKeyDown(event, offset + index),
4081
+ className: cn("relative h-10 overflow-hidden rounded-md border p-0.5 outline-none transition focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", item.selected ? "border-foreground ring-1 ring-foreground" : "border-border hover:border-foreground/60"),
4082
+ children: [/* @__PURE__ */ jsx("span", {
4083
+ className: "grid h-full grid-cols-5 overflow-hidden rounded-[3px]",
4084
+ "aria-hidden": "true",
4085
+ children: item.swatch.map((color, swatchIndex) => /* @__PURE__ */ jsx("span", { style: { backgroundColor: color } }, `${paletteLibraryItemKey(item)}:${swatchIndex}`))
4086
+ }), item.selected ? /* @__PURE__ */ jsx("span", {
4087
+ className: "absolute right-1 top-1 grid size-4 place-items-center rounded-full bg-foreground text-background",
4088
+ children: /* @__PURE__ */ jsx(Check, {
4089
+ "aria-hidden": "true",
4090
+ className: "size-3"
3503
4091
  })
3504
- }) : null
3505
- ]
3506
- })
4092
+ }) : null]
4093
+ }, paletteLibraryItemKey(item)))
4094
+ })]
4095
+ });
4096
+ }
4097
+ function paletteLibraryItemKey(item) {
4098
+ return item.kind === "curated" ? `curated:${item.valueKey}` : `site:${item.paletteId}`;
4099
+ }
4100
+ function selectionForPaletteSnapshot(palette) {
4101
+ switch (palette.kind) {
4102
+ case "curated": return {
4103
+ kind: "curated",
4104
+ valueKey: palette.valueKey
4105
+ };
4106
+ case "curated-fork": return {
4107
+ kind: "curated",
4108
+ valueKey: palette.sourceValueKey
4109
+ };
4110
+ case "site": return {
4111
+ kind: "site",
4112
+ paletteId: palette.paletteId
4113
+ };
4114
+ case "new-site":
4115
+ case "invalid": return null;
4116
+ default: return assertNever$1(palette);
4117
+ }
4118
+ }
4119
+ function PaletteTokenControls({ palette, isCustom, onTokenChange, onReset, disabled }) {
4120
+ return /* @__PURE__ */ jsxs("div", {
4121
+ className: "mt-4 space-y-3 border-t pt-4",
4122
+ children: [/* @__PURE__ */ jsx("fieldset", {
4123
+ disabled,
4124
+ className: "min-w-0 border-0 p-0",
4125
+ children: /* @__PURE__ */ jsx(PaletteTokenEditor, {
4126
+ palette: useMemo(() => paletteColorEntries(palette), [palette]),
4127
+ onChange: onTokenChange,
4128
+ brandRoles: PALETTE_BRAND_ROLES,
4129
+ feedbackRoles: PALETTE_FEEDBACK_ROLES,
4130
+ surfaceRoles: PALETTE_SURFACE_ROLES,
4131
+ showContrastChecks: false,
4132
+ disabled
4133
+ })
4134
+ }), isCustom ? /* @__PURE__ */ jsx("div", {
4135
+ className: "flex justify-end",
4136
+ children: /* @__PURE__ */ jsx(Button, {
4137
+ type: "button",
4138
+ variant: "outline",
4139
+ size: "sm",
4140
+ onClick: onReset,
4141
+ disabled,
4142
+ children: "Reset to variant"
4143
+ })
4144
+ }) : null]
3507
4145
  });
3508
4146
  }
3509
4147
  function PickerOption({ selected, label, description, swatch, onSelect, disabled }) {
@@ -3541,19 +4179,34 @@ function paletteApplyPlanErrorMessage(error) {
3541
4179
  }
3542
4180
  }
3543
4181
  function classifyStyleApplyError(error) {
4182
+ if (error instanceof StyleApplyPlanError) return {
4183
+ kind: "other",
4184
+ message: error.message
4185
+ };
3544
4186
  if (isRecord(error)) {
3545
4187
  const status = error.status;
3546
- const code = error.code;
4188
+ const code = error.code ?? error.errorCode;
4189
+ if (code === "PALETTE_CAP_REACHED") return {
4190
+ kind: "palette-cap",
4191
+ message: "This site has reached its palette limit. Your palette draft is still here."
4192
+ };
3547
4193
  if (status === 409 || code === "resource:conflict") return {
3548
4194
  kind: "stale",
3549
4195
  message: "This design changed elsewhere. Refreshing the latest version now."
3550
4196
  };
3551
4197
  }
4198
+ reportPreviewDesignError("Failed to apply site style selection", error);
3552
4199
  return {
3553
4200
  kind: "other",
3554
- message: error instanceof Error ? error.message : "Unable to apply your selection."
4201
+ message: PREVIEW_DESIGN_SAVE_ERROR_MESSAGE
3555
4202
  };
3556
4203
  }
4204
+ var StyleApplyPlanError = class extends Error {
4205
+ constructor(..._args) {
4206
+ super(..._args);
4207
+ this.name = "StyleApplyPlanError";
4208
+ }
4209
+ };
3557
4210
  function expandedRowForPreviewDesignRow(row) {
3558
4211
  switch (row) {
3559
4212
  case "header": return "header-layout";
@@ -3583,16 +4236,11 @@ function WarningNotice({ warnings, onDismiss }) {
3583
4236
  });
3584
4237
  }
3585
4238
  function describeWarning(warning) {
3586
- switch (warning.kind) {
3587
- case "rejected-personality": return `Button style changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
3588
- case "rejected-palette": return `Palette changed to ${warning.fellBackTo} because ${warning.requested} is not available.`;
3589
- case "rejected-typography-preset": return `Typography changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
3590
- default: return warning;
3591
- }
4239
+ return `Button style changed to ${warning.fellBackTo} because ${warning.requested} is not offered for this site style.`;
3592
4240
  }
3593
4241
  //#endregion
3594
4242
  //#region ../preview-next/src/client/preview/PreviewEditorSidebar.tsx
3595
- function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, pageId, entryData, contentTypeSchema, referenceContentTypeKeys = [], draftContent, currentTemplate, theme, themeId = null, themeSelectionId = null, themeSelectionVersion = null, themeSelectionUpdatedAt = null, themeSelectionButtonPersonalityId = null, runtimePaletteState = null, themeSelectionPaletteVariantId = null, themeSelectionPaletteOverrides = null, themeSelectionHeaderLayoutId = null, themeSelectionHeaderStyleId = null, themeSelectionTypographyPresetId = null, themeSelectionTypographyBodyFontOverride = null, themeSelectionTypographyHeadingFontOverride = null, themeSelectionHeaderLookId = null, themeSelectionFooterLookId = null, themeSelectionHeaderContext, themeSelectionFooterContext, designRowRequest = null, apiClient, bearerToken, supabaseUrl, entrySubrouteContext, assistantPanel, sdkConfig, mediaAssets, bookingOfferingPresets, siteStyleAccess = { kind: "hidden" }, activeTab, onTabChange, onClose, viewportMode, viewportWidth, zoom, onViewportModeChange, onViewportWidthChange, onZoomChange }) {
4243
+ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, pageId, entryData, contentTypeSchema, referenceContentTypeKeys = [], draftContent, currentTemplate, theme, themeId = null, themeSelectionId = null, themeSelectionVersion = null, themeSelectionUpdatedAt = null, themeSelectionButtonPersonalityId = null, runtimePaletteState, themeSelectionHeaderLayoutId = null, themeSelectionHeaderStyleId = null, themeSelectionHeaderLookId = null, themeSelectionFooterLookId = null, themeSelectionHeaderContext, themeSelectionFooterContext, designRowRequest = null, apiClient, bearerToken, supabaseUrl, entrySubrouteContext, assistantPanel, sdkConfig, mediaAssets, bookingOfferingPresets, siteStyleAccess = { kind: "hidden" }, activeTab, onTabChange, onClose, onFooterSaved, viewportMode, viewportWidth, zoom, onViewportModeChange, onViewportWidthChange, onZoomChange }) {
3596
4244
  const controller = useEntryController();
3597
4245
  const controllerBlocks = controller.blocks;
3598
4246
  const uiStore = useEditorUiStoreInstance();
@@ -3603,6 +4251,7 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3603
4251
  const focusRequest = useUISelector(uiStore, (s) => s.focusRequest);
3604
4252
  const activeLinkedEntrySnapshot = useUISelector(uiStore, (s) => s.linkedEntries.getSelectedSnapshot());
3605
4253
  const previewInspection = useUISelector(uiStore, (s) => s.previewInspection);
4254
+ const [designTakeoverActive, setDesignTakeoverActive] = useState(false);
3606
4255
  const { canEdit } = usePreviewAuth();
3607
4256
  const dashboardSetupHandoff = useMemo(() => resolvePreviewDashboardSetupHandoff(), []);
3608
4257
  const dashboardContinueUrl = useMemo(() => buildPreviewDashboardSetupUrl(dashboardBaseUrl, dashboardSetupHandoff), [dashboardBaseUrl, dashboardSetupHandoff]);
@@ -3719,6 +4368,7 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3719
4368
  onBlocksChange: handleBlocksChange,
3720
4369
  metadataPanel: metadataSectionContent,
3721
4370
  designOpenRequestId: designRowRequest?.id ?? null,
4371
+ designTakeoverActive,
3722
4372
  designPanel: canInspectSiteStyle(siteStyleAccess) ? /* @__PURE__ */ jsx(StyleConfigurator, {
3723
4373
  siteId,
3724
4374
  apiClient,
@@ -3732,19 +4382,15 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3732
4382
  selectionUpdatedAt: themeSelectionUpdatedAt,
3733
4383
  buttonPersonalityId: themeSelectionButtonPersonalityId,
3734
4384
  runtimePaletteState,
3735
- paletteVariantId: themeSelectionPaletteVariantId,
3736
- paletteOverrides: themeSelectionPaletteOverrides,
3737
4385
  headerLayoutId: themeSelectionHeaderLayoutId,
3738
4386
  headerStyleId: themeSelectionHeaderStyleId,
3739
- typographyPresetId: themeSelectionTypographyPresetId,
3740
- typographyBodyFontOverride: themeSelectionTypographyBodyFontOverride,
3741
- typographyHeadingFontOverride: themeSelectionTypographyHeadingFontOverride,
3742
4387
  headerLookId: themeSelectionHeaderLookId,
3743
4388
  footerLookId: themeSelectionFooterLookId
3744
4389
  },
3745
4390
  headerSelectionContext: themeSelectionHeaderContext,
3746
4391
  footerSelectionContext: themeSelectionFooterContext,
3747
4392
  expandedRowRequest: designRowRequest,
4393
+ onFocusedSurfaceChange: setDesignTakeoverActive,
3748
4394
  siteStyleAccess
3749
4395
  }) : null,
3750
4396
  sdkConfig: sdkConfig ?? null,
@@ -3767,6 +4413,24 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3767
4413
  })
3768
4414
  })
3769
4415
  });
4416
+ const resolvedEditorPanel = previewInspection.kind === "footer-module" || previewInspection.kind === "footer" ? /* @__PURE__ */ jsx("div", {
4417
+ className: "h-full overflow-y-auto",
4418
+ children: /* @__PURE__ */ jsx(FooterComposerPanel, {
4419
+ apiClient,
4420
+ siteId,
4421
+ customFooterMode: resolvePreviewFooterMode(theme?.footer?.mode),
4422
+ manageLinksHref: `${dashboardBaseUrl?.replace(/\/$/u, "") ?? ""}/dashboard/sites/${siteId}/navigation`,
4423
+ manageSettingsHref: `${dashboardBaseUrl?.replace(/\/$/u, "") ?? ""}/dashboard/sites/${siteId}/settings`,
4424
+ legalRegion: theme?.footer?.bottomBar?.enabled === true ? "separate" : "integrated",
4425
+ initialTarget: previewInspection.kind === "footer-module" ? {
4426
+ kind: "module",
4427
+ moduleId: previewInspection.moduleId
4428
+ } : { kind: "overview" },
4429
+ variant: "sidebar",
4430
+ canEdit,
4431
+ onSaved: onFooterSaved
4432
+ })
4433
+ }) : editorPanel;
3770
4434
  const assistantPanelDisplay = assistantAvailable ? assistantPanel : /* @__PURE__ */ jsx("div", {
3771
4435
  className: "flex h-full flex-col items-center justify-center p-6 text-center text-sm text-muted-foreground",
3772
4436
  children: "AI Assistant is available for pages in preview mode."
@@ -3813,7 +4477,7 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3813
4477
  value: "editor",
3814
4478
  forceMount: true,
3815
4479
  className: "mt-0 min-h-0 flex-1 overflow-hidden data-[state=inactive]:hidden",
3816
- children: editorPanel
4480
+ children: resolvedEditorPanel
3817
4481
  }),
3818
4482
  showAssistantTab ? /* @__PURE__ */ jsx(TabsContent, {
3819
4483
  value: "assistant",
@@ -3825,6 +4489,9 @@ function PreviewEditorSidebar({ surfaceId, siteId, dashboardBaseUrl = null, page
3825
4489
  })
3826
4490
  });
3827
4491
  }
4492
+ function resolvePreviewFooterMode(value) {
4493
+ return value === "blocks" || value === "default+blocks" || value === "none" ? value : "default";
4494
+ }
3828
4495
  function PreviewMetadataPanel({ ownerKind, contentTypeKey, entry }) {
3829
4496
  const uiStore = useEditorUiStoreInstance();
3830
4497
  const currentSlug = useEditorUiStore((state) => state.metadata.getAtPath("draftSlug")) ?? void 0;