@riverbankcms/sdk 0.111.0 → 0.119.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 (196) hide show
  1. package/dist/{PreviewEditorSidebar-CTR6LUhN.mjs → PreviewEditorSidebar-C7TXFitV.mjs} +4 -4
  2. package/dist/{PreviewEditorUI-DZg15Zip.mjs → PreviewEditorUI-_1JIHTXS.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-kkmjklLG.mjs → PreviewQueryProvider-nKUT4TL-.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-DeOTAFvR.mjs → SdkPreviewModeRuntime-CvJTVgWa.mjs} +2579 -34325
  5. package/dist/{SiteChromeCustomizeContext-DOSDLCiJ.mjs → SiteChromeCustomizeContext-66QfCbAS.mjs} +64625 -32791
  6. package/dist/_dts/ai/src/contracts/proposals.d.ts +5211 -639
  7. package/dist/_dts/ai/src/designer/themePatch.d.ts +1 -1
  8. package/dist/_dts/api/src/bookingAdjustments.d.ts +30 -36
  9. package/dist/_dts/api/src/bookingConfig.d.ts +26 -1
  10. package/dist/_dts/api/src/bookingRecords.d.ts +6 -74
  11. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +3 -3
  12. package/dist/_dts/api/src/contentRuntime.d.ts +5 -1
  13. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +4 -4
  14. package/dist/_dts/api/src/endpoints.d.ts +35 -0
  15. package/dist/_dts/api/src/event-presentation.d.ts +4 -0
  16. package/dist/_dts/api/src/index.d.ts +4 -4
  17. package/dist/_dts/api/src/navigation/dashboardBookingsNav.d.ts +4 -1
  18. package/dist/_dts/api/src/navigation/dashboardSettingsNav.d.ts +2 -2
  19. package/dist/_dts/api/src/sdk-event-upsert.d.ts +2 -2
  20. package/dist/_dts/api/src/sdkContracts.d.ts +85 -7
  21. package/dist/_dts/api/src/siteBanner.d.ts +8 -8
  22. package/dist/_dts/api/src/siteOperations.d.ts +1 -1
  23. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +32 -4
  24. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldDefaults.d.ts +3 -12
  25. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldSchema.d.ts +1 -5
  26. package/dist/_dts/blocks/src/bindings/index.d.ts +1 -10
  27. package/dist/_dts/blocks/src/index.d.ts +3 -6
  28. package/dist/_dts/blocks/src/primitives/content/primitives.d.ts +2 -2
  29. package/dist/_dts/blocks/src/system/blocks/body-text.d.ts +2 -2
  30. package/dist/_dts/blocks/src/system/blocks/events/recurring-schedule.d.ts +2 -2
  31. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +2 -8
  32. package/dist/_dts/blocks/src/system/blocks/feature-card-icons.d.ts +2 -2
  33. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +3 -3
  34. package/dist/_dts/blocks/src/system/blocks/form.d.ts +2 -15
  35. package/dist/_dts/blocks/src/system/blocks/intro.d.ts +2 -2
  36. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +66 -66
  37. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +2 -16
  38. package/dist/_dts/blocks/src/system/blocks/site-footer.d.ts +0 -3
  39. package/dist/_dts/blocks/src/system/fragments/index.d.ts +0 -1
  40. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +19 -2
  41. package/dist/_dts/blocks/src/system/manifest/richTextSchema.d.ts +8 -70
  42. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +1 -0
  43. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +6 -0
  44. package/dist/_dts/blocks/src/system/node/schema.d.ts +4 -4
  45. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +1 -10
  46. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +5 -21
  47. package/dist/_dts/blocks/src/system/runtime/nodes/richText-coerce.d.ts +14 -2
  48. package/dist/_dts/blocks/src/system/transforms/typed.d.ts +1 -1
  49. package/dist/_dts/content-editor/src/controller/seedDraftBaselinesFromBlocks.d.ts +1 -1
  50. package/dist/_dts/content-editor/src/hooks/useBlockAutosave.d.ts +17 -1
  51. package/dist/_dts/content-editor/src/hooks/useDebouncedLatestSave.d.ts +1 -1
  52. package/dist/_dts/content-editor/src/hooks/useSimpleAutosave.d.ts +1 -1
  53. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +3 -2
  54. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +2 -2
  55. package/dist/_dts/content-editor/src/ui/stores/autosaveStore.d.ts +1 -1
  56. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +1 -1
  57. package/dist/_dts/content-model/src/content-types/builtins.d.ts +3 -15
  58. package/dist/_dts/content-model/src/content-types/entryValidation.d.ts +2 -0
  59. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  60. package/dist/_dts/content-model/src/templates/defaults.d.ts +1 -21
  61. package/dist/_dts/core/src/editor-state.d.ts +14 -1
  62. package/dist/_dts/core/src/footer-composition.d.ts +2 -1
  63. package/dist/_dts/core/src/index.d.ts +1 -0
  64. package/dist/_dts/core/src/rich-text.d.ts +16 -0
  65. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +189 -86
  66. package/dist/_dts/editor/src/index.d.ts +1 -1
  67. package/dist/_dts/editor/src/primitives/rich-text/utils.d.ts +3 -5
  68. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +1 -1
  69. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +2 -1
  70. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +2 -1
  71. package/dist/_dts/editor-blocks/src/block-editor/blockDraftBaselineSeed.d.ts +15 -0
  72. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockBaselineSync.d.ts +3 -7
  73. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockDuplication.d.ts +12 -0
  74. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +1 -0
  75. package/dist/_dts/editor-blocks/src/block-editor/utils.d.ts +1 -1
  76. package/dist/_dts/editor-blocks/src/index.d.ts +6 -5
  77. package/dist/_dts/editor-blocks/src/widgets/rich-text/RichTextField.d.ts +2 -2
  78. package/dist/_dts/media-core/src/contentMediaAudit.d.ts +104 -0
  79. package/dist/_dts/media-core/src/contentMediaAuditFieldAdapter.d.ts +8 -0
  80. package/dist/_dts/media-core/src/contentMediaSnapshotAudit.d.ts +22 -0
  81. package/dist/_dts/media-core/src/index.d.ts +3 -1
  82. package/dist/_dts/sdk/src/cli/commands/event.d.ts +18 -1
  83. package/dist/_dts/sdk/src/cli/commands/push/execute/theme.d.ts +2 -16
  84. package/dist/_dts/sdk/src/cli/content/reader.d.ts +8 -5
  85. package/dist/_dts/sdk/src/client/management/events.d.ts +2 -1
  86. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  87. package/dist/_dts/sdk/src/client/management/types.d.ts +36 -9
  88. package/dist/_dts/sdk/src/config/typed-entries.d.ts +7 -7
  89. package/dist/_dts/sdk/src/config/validation.d.ts +7 -7
  90. package/dist/_dts/sdk/src/contracts/content.d.ts +5 -15
  91. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  92. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +12 -25
  93. package/dist/_dts/sdk/src/contracts/theme.d.ts +2 -18
  94. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +1 -1
  95. package/dist/_dts/sdk/src/manifest/three-pass-converter.d.ts +4 -4
  96. package/dist/_dts/sdk/src/rendering/helpers/bindings.d.ts +1 -2
  97. package/dist/_dts/sdk/src/rendering/helpers/index.d.ts +1 -1
  98. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +2 -13
  99. package/dist/_dts/sdk/src/theme/index.d.ts +0 -29
  100. package/dist/_dts/sdk/src/version.d.ts +1 -1
  101. package/dist/_dts/site-commands/src/commands.d.ts +31 -18
  102. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +2 -2
  103. package/dist/_dts/theme-core/src/buttons/core.d.ts +0 -1
  104. package/dist/_dts/theme-core/src/buttons/effectClassification.d.ts +22 -9
  105. package/dist/_dts/theme-core/src/buttons/effects/presets/background.d.ts +1 -2
  106. package/dist/_dts/theme-core/src/buttons/effects/presets/border.d.ts +1 -2
  107. package/dist/_dts/theme-core/src/buttons/effects/presets/overlay.d.ts +3 -1
  108. package/dist/_dts/theme-core/src/buttons/effects/presets/shadow.d.ts +2 -2
  109. package/dist/_dts/theme-core/src/buttons/effects/types.d.ts +8 -18
  110. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +2 -2
  111. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +5 -5
  112. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -4
  113. package/dist/_dts/theme-core/src/buttons/parseThemeForPersistence.d.ts +36 -0
  114. package/dist/_dts/theme-core/src/buttons/types.d.ts +1638 -93
  115. package/dist/_dts/theme-core/src/cards/types.d.ts +13 -13
  116. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +969 -69
  117. package/dist/_dts/theme-core/src/navigation/types.d.ts +2 -2
  118. package/dist/_dts/theme-core/src/recommend/generateThemes.d.ts +0 -1
  119. package/dist/_dts/theme-core/src/schema.d.ts +1100 -116
  120. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  121. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +14 -14
  122. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +2 -2
  123. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  124. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +6 -0
  125. package/dist/cli/index.mjs +5706 -4852
  126. package/dist/cli/init-docs/content/cli-reference.md +40 -1
  127. package/dist/cli/init-docs/content/content-management.md +6 -0
  128. package/dist/client/{DiscountCodeDisclosure-C4xvTBux.mjs → DiscountCodeDisclosure-DIWj-P9v.mjs} +1 -1
  129. package/dist/client/{EventCombined.client-Cr7G131j.mjs → EventCombined.client-De8YWBSr.mjs} +2 -2
  130. package/dist/client/{EventListing.client-CAjbseNy.mjs → EventListing.client-seHTjimY.mjs} +2 -2
  131. package/dist/client/{LeafletStaticMap-BMlWfwz3.mjs → LeafletStaticMap-BmQxFcJW.mjs} +1 -1
  132. package/dist/client/{MagicLinkForm-ChdFz96y.mjs → MagicLinkForm-OoJAIUIj.mjs} +1 -1
  133. package/dist/client/{StepIndicator-7D3GaBeM.mjs → StepIndicator-CiZ8kdPk.mjs} +2 -2
  134. package/dist/client/{accordion-D8JTs2Si.mjs → accordion-6lnN__XJ.mjs} +1 -1
  135. package/dist/client/{bookingForm-H3DCRBHs.mjs → bookingForm-5uyqhONU.mjs} +6 -6
  136. package/dist/client/bookings.mjs +2219 -527
  137. package/dist/client/{carousel-DMr_QWlz.mjs → carousel-Dtnifil_.mjs} +1 -1
  138. package/dist/client/{cart-DWMbfBdR.mjs → cart-5Tc5S7NV.mjs} +3 -3
  139. package/dist/client/{checkout-zmfdkXLx.mjs → checkout-CYKKN5Hr.mjs} +3 -3
  140. package/dist/client/client.mjs +2498 -2384
  141. package/dist/client/{colorTokenParts-FSS3al6l.mjs → colorStyles-BWpj_h4C.mjs} +88 -1
  142. package/dist/client/{courseRegistration-Z_mn18jy.mjs → courseRegistration-CxCBybB4.mjs} +6 -6
  143. package/dist/client/{eventCalendar-CYp0YJGF.mjs → eventCalendar-Xj2jZS9X.mjs} +4 -4
  144. package/dist/client/{eventCombined-rKyYZUa9.mjs → eventCombined-CgcMoOgM.mjs} +2 -2
  145. package/dist/client/{eventDetails-X0ZaUuMj.mjs → eventDetails-CEAH8Edc.mjs} +3 -3
  146. package/dist/client/{eventListing-W1SuL7EP.mjs → eventListing-qyPVQ0wP.mjs} +2 -2
  147. package/dist/client/{eventRegistration-ClWricui.mjs → eventRegistration-DDu79CtL.mjs} +6 -6
  148. package/dist/client/{form--eKPwAAD.mjs → form-Bc7JKI7I.mjs} +1 -1
  149. package/dist/client/{headerSection-BSeGr-FS.mjs → headerSection-vTku_LEl.mjs} +2 -2
  150. package/dist/client/hooks.mjs +373 -555
  151. package/dist/client/{locationMap-BpOrtNYn.mjs → locationMap-Bg7X4Jna.mjs} +3 -3
  152. package/dist/client/{media-DoF0dcVx.mjs → media-CNE1Z-wb.mjs} +1 -2
  153. package/dist/client/{newsletterForm-Bt7it7Dy.mjs → newsletterForm-lpylVdp5.mjs} +1 -1
  154. package/dist/client/{pricing-BTjSDDr4.mjs → pricing-DE8fjJiT.mjs} +1 -1
  155. package/dist/client/{productDetail-ICnVTcCg.mjs → productDetail-D7Bo2RMH.mjs} +3 -3
  156. package/dist/client/{productList-ZHr6D604.mjs → productList-VjnZqiqN.mjs} +3 -3
  157. package/dist/client/{propsDecoder-wWN0tBDF.mjs → propsDecoder-DreFXjNP.mjs} +2212 -621
  158. package/dist/client/rendering/client.mjs +21612 -21646
  159. package/dist/client/rendering/islands.mjs +20 -20
  160. package/dist/client/rendering.mjs +1571 -1547
  161. package/dist/client/{shop-C1u_BUn0.mjs → shop-IRsHHbf7.mjs} +5 -5
  162. package/dist/client/{shop-commerce-CleZdRha.mjs → shop-commerce-gnI8PieZ.mjs} +27 -10
  163. package/dist/client/{shop-commerce-store-ycnoB2Ce.mjs → shop-commerce-store-BTbrhObn.mjs} +1 -1
  164. package/dist/client/{shopShared-CPhByt2a.mjs → shopShared-D6KS2_qk.mjs} +3 -3
  165. package/dist/client/{useDiscountCodeUi-cStSQI6C.mjs → useDiscountCodeUi-DCccGCxV.mjs} +1 -1
  166. package/dist/client/{useEventFilters-DKPN7hSY.mjs → useEventFilters-eGs3rPZQ.mjs} +3 -104
  167. package/dist/client/{usePolling-BKAd6pkZ.mjs → usePolling-CvSU2sur.mjs} +2 -2
  168. package/dist/preview-next/before-render.mjs +57 -0
  169. package/dist/preview-next/client/runtime.mjs +4 -4
  170. package/dist/preview-next/middleware.mjs +57 -0
  171. package/dist/{previewAuthStore-BkT1D06A.mjs → previewAuthStore-DN7vcRsv.mjs} +57 -0
  172. package/dist/{sdk-runtime-hu3wr3ng.mjs → sdk-runtime-DaOiA7ct.mjs} +2 -2
  173. package/dist/{separator-Bln8izQ9.mjs → separator-CqRhcCxD.mjs} +1 -1
  174. package/dist/server/components.mjs +4092 -4108
  175. package/dist/server/config-template-validation.mjs +4 -23
  176. package/dist/server/config-validation.mjs +373 -555
  177. package/dist/server/config.mjs +410 -558
  178. package/dist/server/data.mjs +373 -555
  179. package/dist/server/index.mjs +58 -1
  180. package/dist/server/next.mjs +32958 -32926
  181. package/dist/server/page-converter.mjs +369 -526
  182. package/dist/server/prebuild.mjs +1 -1
  183. package/dist/server/rendering/server.mjs +5371 -3174
  184. package/dist/server/rendering.mjs +4099 -4124
  185. package/dist/server/routing.mjs +758 -966
  186. package/dist/server/server.mjs +448 -632
  187. package/dist/server/theme-bridge.mjs +472 -525
  188. package/dist/server/theme.mjs +1074 -392
  189. package/package.json +1 -1
  190. package/dist/_dts/blocks/src/primitives/content/components/Button.d.ts +0 -40
  191. package/dist/_dts/blocks/src/system/fragments/library/footerBottomText.d.ts +0 -19
  192. package/dist/_dts/media-core/src/legacyMediaValue.d.ts +0 -11
  193. package/dist/_dts/theme-core/src/styles/buttons.d.ts +0 -27
  194. package/dist/_dts/theme-core/src/styles/buttonsLibrary.d.ts +0 -2
  195. package/dist/_dts/theme-core/src/styles/glow.d.ts +0 -3
  196. package/dist/client/colorStyles-gcTh5yZT.mjs +0 -89
@@ -6687,6 +6687,63 @@ const API_ENDPOINTS$1 = {
6687
6687
  auth: "service",
6688
6688
  responseKind: "json"
6689
6689
  },
6690
+ sdkListEventOccurrences: {
6691
+ path: "/sdk/{siteId}/events/{identifier}/occurrences",
6692
+ method: "GET",
6693
+ auth: "service",
6694
+ responseKind: "json",
6695
+ errors: [
6696
+ "resource:not_found",
6697
+ "validation:invalid_input",
6698
+ "server:internal_error"
6699
+ ]
6700
+ },
6701
+ sdkAddEventOccurrence: {
6702
+ path: "/sdk/{siteId}/events/{identifier}/occurrences",
6703
+ method: "POST",
6704
+ auth: "service",
6705
+ responseKind: "json",
6706
+ errors: [
6707
+ "resource:not_found",
6708
+ "validation:invalid_input",
6709
+ "resource:conflict",
6710
+ "server:internal_error"
6711
+ ]
6712
+ },
6713
+ sdkUpdateEventOccurrence: {
6714
+ path: "/sdk/{siteId}/events/{identifier}/occurrences/{occurrenceId}",
6715
+ method: "PATCH",
6716
+ auth: "service",
6717
+ responseKind: "json",
6718
+ errors: [
6719
+ "resource:not_found",
6720
+ "validation:invalid_input",
6721
+ "resource:conflict",
6722
+ "server:internal_error"
6723
+ ]
6724
+ },
6725
+ sdkCancelEventOccurrence: {
6726
+ path: "/sdk/{siteId}/events/{identifier}/occurrences/{occurrenceId}/cancel",
6727
+ method: "POST",
6728
+ auth: "service",
6729
+ responseKind: "json",
6730
+ errors: [
6731
+ "resource:not_found",
6732
+ "resource:conflict",
6733
+ "server:internal_error"
6734
+ ]
6735
+ },
6736
+ sdkDeleteEventOccurrence: {
6737
+ path: "/sdk/{siteId}/events/{identifier}/occurrences/{occurrenceId}",
6738
+ method: "DELETE",
6739
+ auth: "service",
6740
+ responseKind: "json",
6741
+ errors: [
6742
+ "resource:not_found",
6743
+ "resource:conflict",
6744
+ "server:internal_error"
6745
+ ]
6746
+ },
6690
6747
  sdkListCourses: {
6691
6748
  path: "/sdk/{siteId}/courses",
6692
6749
  method: "GET",
@@ -7434,17 +7491,17 @@ function buildEndpointURL(baseURL, endpoint) {
7434
7491
  function isFormDataBody(value) {
7435
7492
  return typeof FormData !== "undefined" && value instanceof FormData;
7436
7493
  }
7437
- function isUnknownArray$1(value) {
7494
+ function isUnknownArray(value) {
7438
7495
  return Array.isArray(value);
7439
7496
  }
7440
7497
  function appendQueryParam(queryParams, key, value) {
7441
7498
  if (value === void 0 || value === null) return;
7442
- if (isUnknownArray$1(value)) {
7499
+ if (isUnknownArray(value)) {
7443
7500
  const stack = [...value].reverse();
7444
7501
  while (stack.length > 0) {
7445
7502
  const item = stack.pop();
7446
7503
  if (item === void 0 || item === null) continue;
7447
- if (isUnknownArray$1(item)) {
7504
+ if (isUnknownArray(item)) {
7448
7505
  stack.push(...[...item].reverse());
7449
7506
  continue;
7450
7507
  }
@@ -7817,7 +7874,7 @@ var SimpleCache = class {
7817
7874
  * 1. This constant
7818
7875
  * 2. The "version" field in package.json
7819
7876
  */
7820
- const SDK_VERSION = "0.111.0";
7877
+ const SDK_VERSION = "0.119.0";
7821
7878
  //#endregion
7822
7879
  //#region src/client/error.ts
7823
7880
  let _Symbol$for;
@@ -13461,151 +13518,6 @@ z$1.discriminatedUnion("kind", [
13461
13518
  })
13462
13519
  ]);
13463
13520
  //#endregion
13464
- //#region ../theme-core/src/surfaces/foregroundContract.ts
13465
- const SURFACE_FOREGROUND_CSS_VARS = {
13466
- text: "--rb-surface-text-color",
13467
- heading: "--rb-surface-heading-color",
13468
- link: "--rb-surface-link-color",
13469
- supporting: "--rb-surface-supporting-color"
13470
- };
13471
- const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
13472
- const DEFAULT_ROLE_FALLBACKS = {
13473
- text: DEFAULT_SURFACE_TEXT_COLOR,
13474
- heading: DEFAULT_SURFACE_TEXT_COLOR,
13475
- link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
13476
- supporting: "rgb(var(--tb-mutedText, 100 116 139))"
13477
- };
13478
- function requireSurfaceColorValue(role, value) {
13479
- const normalized = value.trim();
13480
- if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
13481
- return normalized;
13482
- }
13483
- function completeSurfaceForegrounds(input) {
13484
- const text = requireSurfaceColorValue("text", input.text);
13485
- return {
13486
- text,
13487
- heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
13488
- link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
13489
- supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
13490
- };
13491
- }
13492
- function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
13493
- const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
13494
- const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
13495
- if (role === "text") return `var(${roleVariable}, ${fallback})`;
13496
- return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
13497
- }
13498
- function surfaceForegroundValueWithOpacity(role, opacityPercent) {
13499
- if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
13500
- return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
13501
- }
13502
- function surfaceForegroundStyle(input) {
13503
- const foregrounds = completeSurfaceForegrounds(input);
13504
- return {
13505
- color: foregrounds.text,
13506
- [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
13507
- [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
13508
- [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
13509
- [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
13510
- };
13511
- }
13512
- //#endregion
13513
- //#region ../theme-core/src/utils/colorStyles.ts
13514
- const COLOR_VAR_PREFIX = "--tb-";
13515
- function parseToken(source) {
13516
- const parts = parseColorTokenParts(source);
13517
- if (parts.kind === "raw") return {
13518
- token: source,
13519
- raw: parts.value
13520
- };
13521
- if (parts.kind === "invalid") return { token: source };
13522
- return {
13523
- token: parts.token,
13524
- opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
13525
- };
13526
- }
13527
- const SURFACE_TEXT_ROLE_BY_TOKEN = {
13528
- text: "text",
13529
- heading: "heading",
13530
- mutedText: "supporting"
13531
- };
13532
- function surfaceRoleForTextToken(token) {
13533
- return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
13534
- }
13535
- /**
13536
- * Converts a theme color token to a CSS rgb value.
13537
- * This is the primary function for applying theme colors to CSS properties.
13538
- *
13539
- * @param token - Theme color token (semantic, with opacity, or raw)
13540
- * @returns CSS color value (rgb with optional alpha, or raw CSS value)
13541
- *
13542
- * @example
13543
- * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
13544
- * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
13545
- * rgbColorValue('raw:transparent') // => 'transparent'
13546
- */
13547
- function rgbColorValue(token) {
13548
- const { token: baseToken, opacityPercent, raw } = parseToken(token);
13549
- if (raw) return raw;
13550
- const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
13551
- if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
13552
- return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
13553
- }
13554
- /**
13555
- * Creates a backgroundColor style object from a theme token.
13556
- *
13557
- * @param token - Theme color token
13558
- * @returns CSSProperties with backgroundColor
13559
- */
13560
- function backgroundColorStyle(token) {
13561
- return { backgroundColor: rgbColorValue(token) };
13562
- }
13563
- /**
13564
- * Creates a text color style object from a theme token.
13565
- *
13566
- * @param token - Theme color token
13567
- * @returns CSSProperties with color
13568
- */
13569
- function textColorStyle(token) {
13570
- const parsed = parseToken(token);
13571
- const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
13572
- if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
13573
- return { color: rgbColorValue(token) };
13574
- }
13575
- /**
13576
- * Creates a border color style object from a theme token.
13577
- *
13578
- * @param token - Theme color token
13579
- * @returns CSSProperties with borderColor
13580
- */
13581
- function borderColorStyle(token) {
13582
- return { borderColor: rgbColorValue(token) };
13583
- }
13584
- /**
13585
- * Merges multiple CSSProperties objects into a single object.
13586
- * Later styles override earlier ones. Undefined values are filtered out.
13587
- * Returns undefined if the merged result is empty.
13588
- *
13589
- * @param styles - Variable number of CSSProperties objects (can be undefined)
13590
- * @returns Merged CSSProperties object, or undefined if empty
13591
- *
13592
- * @example
13593
- * mergeStyles(
13594
- * backgroundColorStyle('surface'),
13595
- * textColorStyle('text'),
13596
- * borderColorStyle('border')
13597
- * )
13598
- * // => { backgroundColor: '...', color: '...', borderColor: '...' }
13599
- */
13600
- function mergeStyles(...styles) {
13601
- const merged = {};
13602
- for (const style of styles) {
13603
- if (!style) continue;
13604
- Object.assign(merged, style);
13605
- }
13606
- return Object.keys(merged).length ? merged : void 0;
13607
- }
13608
- //#endregion
13609
13521
  //#region ../theme-core/src/lib/media.ts
13610
13522
  const hotspotSchema$1 = z.object({
13611
13523
  x: z.number().min(0).max(1),
@@ -13663,111 +13575,6 @@ const mediaSchema$1 = z.object({
13663
13575
  transform: transformSchema$1.optional()
13664
13576
  });
13665
13577
  //#endregion
13666
- //#region ../theme-core/src/buttons/effectClassification.ts
13667
- /**
13668
- * Exhaustive live policy for every registered effect id.
13669
- *
13670
- * The resolver consumes the transition payload directly, so adding an effect
13671
- * cannot update classification without also defining its runtime behavior.
13672
- */
13673
- const BUTTON_EFFECT_POLICIES = {
13674
- "darken-background": {
13675
- classification: "colour-transition",
13676
- transition: {
13677
- kind: "adjust-shade",
13678
- direction: "darker"
13679
- }
13680
- },
13681
- "lighten-background": {
13682
- classification: "colour-transition",
13683
- transition: {
13684
- kind: "adjust-shade",
13685
- direction: "lighter"
13686
- }
13687
- },
13688
- "background-on-hover": {
13689
- classification: "colour-transition",
13690
- transition: { kind: "replace-background" }
13691
- },
13692
- "background-on-hover-alpha": {
13693
- classification: "unsupported-colour",
13694
- reason: "alpha_requires_surface"
13695
- },
13696
- "fade-background": {
13697
- classification: "unsupported-colour",
13698
- reason: "changes_element_opacity"
13699
- },
13700
- "opacity-reduce": {
13701
- classification: "unsupported-colour",
13702
- reason: "changes_element_opacity"
13703
- },
13704
- "gradient-text": {
13705
- classification: "unsupported-colour",
13706
- reason: "changes_foreground"
13707
- },
13708
- "text-color-change": {
13709
- classification: "unsupported-colour",
13710
- reason: "changes_foreground"
13711
- },
13712
- "border-color-change": {
13713
- classification: "unsupported-colour",
13714
- reason: "changes_border"
13715
- },
13716
- "aurora-glow": {
13717
- classification: "unsupported-background-overlay",
13718
- reason: "unresolved_background_overlay"
13719
- },
13720
- "cosmic-stardust": {
13721
- classification: "unsupported-background-overlay",
13722
- reason: "unresolved_background_overlay"
13723
- },
13724
- "gradient-border-fill": {
13725
- classification: "unsupported-background-overlay",
13726
- reason: "unresolved_background_overlay"
13727
- },
13728
- "animated-gradient-border-fill": {
13729
- classification: "unsupported-background-overlay",
13730
- reason: "unresolved_background_overlay"
13731
- },
13732
- "nav-underline": {
13733
- classification: "not-button",
13734
- reason: "not_button_effect"
13735
- },
13736
- "nav-frosted-base": {
13737
- classification: "not-button",
13738
- reason: "not_button_effect"
13739
- },
13740
- "nav-frosted-hover": {
13741
- classification: "not-button",
13742
- reason: "not_button_effect"
13743
- },
13744
- "nav-frosted-active": {
13745
- classification: "not-button",
13746
- reason: "not_button_effect"
13747
- },
13748
- "animated-gradient": { classification: "non-colour" },
13749
- "border-glow": { classification: "non-colour" },
13750
- "drop-shadow": { classification: "non-colour" },
13751
- "inner-shadow": { classification: "non-colour" },
13752
- "neumorphic-shadow": { classification: "non-colour" },
13753
- "outer-glow": { classification: "non-colour" },
13754
- "retro-3d-shadow": { classification: "non-colour" },
13755
- "scale-up": { classification: "non-colour" },
13756
- "scale-down": { classification: "non-colour" },
13757
- lift: { classification: "non-colour" },
13758
- "press-down": { classification: "non-colour" },
13759
- pop: { classification: "non-colour" },
13760
- "border-bottom-grow": { classification: "non-colour" },
13761
- "sketchy-border": { classification: "non-colour" },
13762
- "pulse-animation": { classification: "non-colour" }
13763
- };
13764
- function getButtonEffectPolicy(effectId) {
13765
- return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
13766
- }
13767
- function classifyButtonEffectId(effectId) {
13768
- return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
13769
- }
13770
- //#endregion
13771
13578
  //#region ../theme-core/src/buttons/types.ts
13772
13579
  /**
13773
13580
  * Standard semantic variant roles a theme exposes.
@@ -13784,6 +13591,13 @@ const VARIANT_ROLES = [
13784
13591
  "tertiary",
13785
13592
  "accent"
13786
13593
  ];
13594
+ /**
13595
+ * Canonical persisted/CSS-safe button variant identifier.
13596
+ *
13597
+ * IDs become class selectors, so keep the contract deliberately narrower than
13598
+ * an arbitrary string and shared with every authoring/runtime boundary.
13599
+ */
13600
+ const buttonVariantIdSchema = z$1.string().min(1).max(50).regex(/^[a-z0-9-]+$/, "Button variant IDs may contain lowercase letters, numbers, and hyphens only");
13787
13601
  function parseOpaqueButtonColourToken(value) {
13788
13602
  const token = parseThemeColorToken(value);
13789
13603
  if (token === null) return null;
@@ -13798,6 +13612,109 @@ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => pa
13798
13612
  if (token === null) throw new Error("Invariant: opaque button token passed its schema");
13799
13613
  return token;
13800
13614
  });
13615
+ const buttonShadeAmountSchema = z$1.number().finite().min(0).max(900).multipleOf(100);
13616
+ const buttonShadowIntensitySchema = z$1.enum([
13617
+ "subtle",
13618
+ "medium",
13619
+ "strong"
13620
+ ]);
13621
+ /**
13622
+ * The complete persisted button-effect contract.
13623
+ *
13624
+ * Navigation has its own generic interactive-effect model. Button variants use
13625
+ * this closed union so unsupported effect IDs, unknown option keys, and unsafe
13626
+ * CSS values cannot become representable after the schema boundary.
13627
+ */
13628
+ const buttonEffectApplicationSchema = z$1.discriminatedUnion("effectId", [
13629
+ z$1.strictObject({
13630
+ effectId: z$1.literal("darken-background"),
13631
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
13632
+ }),
13633
+ z$1.strictObject({
13634
+ effectId: z$1.literal("lighten-background"),
13635
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
13636
+ }),
13637
+ z$1.strictObject({
13638
+ effectId: z$1.literal("background-on-hover"),
13639
+ options: z$1.strictObject({ colorToken: opaqueButtonColourTokenSchema.optional() }).optional()
13640
+ }),
13641
+ z$1.strictObject({
13642
+ effectId: z$1.literal("animated-gradient"),
13643
+ options: z$1.strictObject({ direction: z$1.enum(["horizontal", "vertical"]).optional() }).optional()
13644
+ }),
13645
+ z$1.strictObject({
13646
+ effectId: z$1.literal("drop-shadow"),
13647
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
13648
+ }),
13649
+ z$1.strictObject({
13650
+ effectId: z$1.literal("inner-shadow"),
13651
+ options: z$1.strictObject({}).optional()
13652
+ }),
13653
+ z$1.strictObject({
13654
+ effectId: z$1.literal("neumorphic-shadow"),
13655
+ options: z$1.strictObject({
13656
+ mode: z$1.enum(["raised", "pressed"]).optional(),
13657
+ intensity: buttonShadowIntensitySchema.optional()
13658
+ }).optional()
13659
+ }),
13660
+ z$1.strictObject({
13661
+ effectId: z$1.literal("retro-3d-shadow"),
13662
+ options: z$1.strictObject({}).optional()
13663
+ }),
13664
+ z$1.strictObject({
13665
+ effectId: z$1.literal("scale-up"),
13666
+ options: z$1.strictObject({ scale: z$1.enum([
13667
+ "1.02",
13668
+ "1.03",
13669
+ "1.05",
13670
+ "1.10"
13671
+ ]).optional() }).optional()
13672
+ }),
13673
+ z$1.strictObject({
13674
+ effectId: z$1.literal("scale-down"),
13675
+ options: z$1.strictObject({ scale: z$1.enum([
13676
+ "0.90",
13677
+ "0.95",
13678
+ "0.97",
13679
+ "0.98"
13680
+ ]).optional() }).optional()
13681
+ }),
13682
+ z$1.strictObject({
13683
+ effectId: z$1.literal("lift"),
13684
+ options: z$1.strictObject({ amount: z$1.enum([
13685
+ "0.25",
13686
+ "0.5",
13687
+ "1"
13688
+ ]).optional() }).optional()
13689
+ }),
13690
+ z$1.strictObject({
13691
+ effectId: z$1.literal("press-down"),
13692
+ options: z$1.strictObject({}).optional()
13693
+ }),
13694
+ z$1.strictObject({
13695
+ effectId: z$1.literal("pop"),
13696
+ options: z$1.strictObject({ intensity: z$1.enum([
13697
+ "subtle",
13698
+ "medium",
13699
+ "dramatic"
13700
+ ]).optional() }).optional()
13701
+ }),
13702
+ z$1.strictObject({
13703
+ effectId: z$1.literal("border-bottom-grow"),
13704
+ options: z$1.strictObject({
13705
+ height: z$1.union([z$1.literal(1), z$1.literal(2)]).optional(),
13706
+ origin: z$1.enum([
13707
+ "left",
13708
+ "center",
13709
+ "right"
13710
+ ]).optional()
13711
+ }).optional()
13712
+ }),
13713
+ z$1.strictObject({
13714
+ effectId: z$1.literal("sketchy-border"),
13715
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
13716
+ })
13717
+ ]);
13801
13718
  const cornerStyleSchema = z$1.enum([
13802
13719
  "square",
13803
13720
  "rounded",
@@ -13832,13 +13749,7 @@ const hoverTransformSchema = z$1.enum([
13832
13749
  "scale",
13833
13750
  "press"
13834
13751
  ]);
13835
- const hoverColorSchema = z$1.enum([
13836
- "none",
13837
- "brighten",
13838
- "darken",
13839
- "saturate",
13840
- "token"
13841
- ]);
13752
+ z$1.enum(["none", "token"]);
13842
13753
  const buttonPaddingPresetSchema = z$1.enum([
13843
13754
  "compact",
13844
13755
  "default",
@@ -13899,7 +13810,7 @@ const buttonSizesSchema = z$1.object({
13899
13810
  lg,
13900
13811
  xl: xl ?? lg
13901
13812
  }));
13902
- const canonicalButtonGlobalSettingsSchema = z$1.object({
13813
+ const buttonGlobalSettingsBaseSchema = z$1.strictObject({
13903
13814
  cornerStyle: cornerStyleSchema,
13904
13815
  cornerRadius: z$1.number().min(0).max(24).optional(),
13905
13816
  /**
@@ -13912,30 +13823,23 @@ const canonicalButtonGlobalSettingsSchema = z$1.object({
13912
13823
  shadow: shadowSizeSchema.optional(),
13913
13824
  textTransform: textTransformSchema,
13914
13825
  fontWeight: fontWeightSchema,
13915
- /**
13916
- * @deprecated Set per-variant via `variant.border.widthClass` instead.
13917
- * Schema field is preserved so existing presets parse; the value is unread
13918
- * by the current generator and will be removed once the legacy
13919
- * `primitives/content/components/Button.tsx` slot exports are deleted.
13920
- */
13921
- borderWidth: z$1.union([
13922
- z$1.literal(1),
13923
- z$1.literal(2),
13924
- z$1.literal(3)
13925
- ]),
13926
13826
  typography: buttonTypographySchema.optional(),
13927
13827
  fontSize: z$1.string().optional(),
13928
13828
  letterSpacing: letterSpacingSchema.optional(),
13929
13829
  italic: z$1.boolean().optional(),
13930
13830
  paddingPreset: buttonPaddingPresetSchema.optional(),
13931
13831
  hoverTransform: hoverTransformSchema,
13932
- hoverColor: hoverColorSchema,
13933
- hoverColorToken: opaqueButtonColourTokenSchema.optional(),
13934
13832
  gradientStyle: gradientStyleSchema.optional(),
13935
13833
  gradientAngle: z$1.number().min(0).max(360).optional(),
13936
13834
  gradientSharpness: gradientSharpnessSchema.optional()
13937
13835
  });
13938
- const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
13836
+ const buttonGlobalSettingsSchema = z$1.discriminatedUnion("hoverColor", [buttonGlobalSettingsBaseSchema.extend({
13837
+ hoverColor: z$1.literal("none"),
13838
+ hoverColorToken: z$1.never().optional()
13839
+ }), buttonGlobalSettingsBaseSchema.extend({
13840
+ hoverColor: z$1.literal("token"),
13841
+ hoverColorToken: opaqueButtonColourTokenSchema
13842
+ })]);
13939
13843
  const gradientDirectionSchema$1 = z$1.enum([
13940
13844
  "to-r",
13941
13845
  "to-l",
@@ -13947,11 +13851,11 @@ const gradientDirectionSchema$1 = z$1.enum([
13947
13851
  "to-bl"
13948
13852
  ]);
13949
13853
  const buttonColourTreatmentSchema = z$1.discriminatedUnion("type", [
13950
- z$1.object({
13854
+ z$1.strictObject({
13951
13855
  type: z$1.literal("solid"),
13952
13856
  colorToken: opaqueButtonColourTokenSchema
13953
13857
  }),
13954
- z$1.object({
13858
+ z$1.strictObject({
13955
13859
  type: z$1.literal("gradient"),
13956
13860
  stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
13957
13861
  const [first, second, ...rest] = stops;
@@ -13965,19 +13869,15 @@ const buttonColourTreatmentSchema = z$1.discriminatedUnion("type", [
13965
13869
  direction: gradientDirectionSchema$1,
13966
13870
  foregroundToken: opaqueButtonColourTokenSchema
13967
13871
  }),
13968
- z$1.object({
13872
+ z$1.strictObject({
13969
13873
  type: z$1.literal("surface"),
13970
13874
  role: z$1.literal("link")
13971
13875
  })
13972
13876
  ]);
13973
13877
  const buttonBackgroundSchema = buttonColourTreatmentSchema;
13974
- const effectApplicationSchema$1 = z$1.object({
13975
- effectId: z$1.string(),
13976
- options: z$1.record(z$1.string(), z$1.json()).optional()
13977
- });
13978
- const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
13979
- const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
13980
- const variantShadowSchema = z$1.object({
13878
+ const buttonBorderSchema = z$1.strictObject({ widthClass: z$1.string() });
13879
+ const buttonVariantAffordanceSchema = z$1.strictObject({ iconId: z$1.string().min(1) });
13880
+ const variantShadowSchema = z$1.strictObject({
13981
13881
  elevation: z$1.enum([
13982
13882
  "none",
13983
13883
  "low",
@@ -13990,13 +13890,13 @@ const variantShadowSchema = z$1.object({
13990
13890
  "hard"
13991
13891
  ]).nullable(),
13992
13892
  position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
13993
- colorToken: themeColorTokenSchema.optional()
13893
+ colorToken: opaqueButtonColourTokenSchema.optional()
13994
13894
  });
13995
- const variantEffectsSchema = z$1.object({
13996
- base: z$1.array(effectApplicationSchema$1).optional(),
13997
- hover: z$1.array(effectApplicationSchema$1).optional(),
13998
- active: z$1.array(effectApplicationSchema$1).optional(),
13999
- focus: z$1.array(effectApplicationSchema$1).optional()
13895
+ const variantEffectsSchema = z$1.strictObject({
13896
+ base: z$1.array(buttonEffectApplicationSchema).optional(),
13897
+ hover: z$1.array(buttonEffectApplicationSchema).optional(),
13898
+ active: z$1.array(buttonEffectApplicationSchema).optional(),
13899
+ focus: z$1.array(buttonEffectApplicationSchema).optional()
14000
13900
  });
14001
13901
  /**
14002
13902
  * Per-variant size overrides schema — every entry is optional.
@@ -14017,8 +13917,8 @@ const variantSizeOverridesSchema = z$1.object({
14017
13917
  * default (e.g. link-style variants declaring zero horizontal padding at
14018
13918
  * every size).
14019
13919
  */
14020
- const canonicalButtonVariantSchema = z$1.object({
14021
- id: z$1.string(),
13920
+ const buttonVariantSchema = z$1.strictObject({
13921
+ id: buttonVariantIdSchema,
14022
13922
  name: z$1.string().min(1).max(20),
14023
13923
  enabled: z$1.boolean(),
14024
13924
  priority: prioritySchema,
@@ -14032,154 +13932,73 @@ const canonicalButtonVariantSchema = z$1.object({
14032
13932
  sizes: variantSizeOverridesSchema.optional(),
14033
13933
  affordance: buttonVariantAffordanceSchema.optional()
14034
13934
  });
14035
- /**
14036
- * Temporary persisted-theme admission boundary for issue #1206.
14037
- *
14038
- * Production inventory on 2026-07-22 found 22 rows using the old independent
14039
- * `textColorToken`/border colour shape. New source and all downstream code use
14040
- * only the canonical relationship. Delete this preprocessor after the reviewed
14041
- * production rewrite reports zero legacy rows.
14042
- */
14043
- const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
14044
- const buttonSystemSchema = z$1.object({
13935
+ const buttonSystemSchema = z$1.strictObject({
14045
13936
  global: buttonGlobalSettingsSchema,
14046
13937
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
14047
13938
  sizes: buttonSizesSchema.optional()
13939
+ }).superRefine((system, ctx) => {
13940
+ const seen = /* @__PURE__ */ new Set();
13941
+ system.variants.forEach((variant, index) => {
13942
+ if (seen.has(variant.id)) ctx.addIssue({
13943
+ code: "custom",
13944
+ path: [
13945
+ "variants",
13946
+ index,
13947
+ "id"
13948
+ ],
13949
+ message: `Duplicate button variant ID: ${variant.id}`
13950
+ });
13951
+ seen.add(variant.id);
13952
+ });
14048
13953
  });
14049
- function normalizeLegacyButtonVariantInput(input) {
14050
- if (!isUnknownRecord(input)) return input;
14051
- const background = input.background;
14052
- if (!isUnknownRecord(background)) return input;
14053
- if (!(background.type === "transparent" || Object.prototype.hasOwnProperty.call(input, "textColorToken") || isUnknownRecord(input.border) && Object.prototype.hasOwnProperty.call(input.border, "colorToken") || hasLegacyButtonEffects(input.effects) || hasLegacyAlphaToken(input, background))) return input;
14054
- const { textColorToken, ...withoutLegacyText } = input;
14055
- const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
14056
- const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
14057
- const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
14058
- const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
14059
- if (background.type === "transparent") return {
14060
- ...withoutLegacyText,
14061
- background: {
14062
- type: "surface",
14063
- role: "link"
14064
- },
14065
- border: normalizedBorder,
14066
- affordance: normalizedAffordance,
14067
- effects: normalizedEffects,
14068
- hoverBackgroundToken
14069
- };
14070
- if (background.type === "gradient") {
14071
- const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
14072
- return {
14073
- ...withoutLegacyText,
14074
- background: {
14075
- ...background,
14076
- stops,
14077
- foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
14078
- },
14079
- border: normalizedBorder,
14080
- affordance: normalizedAffordance,
14081
- effects: normalizedEffects,
14082
- hoverBackgroundToken
14083
- };
14084
- }
13954
+ //#endregion
13955
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
13956
+ const SURFACE_FOREGROUND_CSS_VARS = {
13957
+ text: "--rb-surface-text-color",
13958
+ heading: "--rb-surface-heading-color",
13959
+ link: "--rb-surface-link-color",
13960
+ supporting: "--rb-surface-supporting-color"
13961
+ };
13962
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
13963
+ const DEFAULT_ROLE_FALLBACKS = {
13964
+ text: DEFAULT_SURFACE_TEXT_COLOR,
13965
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
13966
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
13967
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
13968
+ };
13969
+ function requireSurfaceColorValue(role, value) {
13970
+ const normalized = value.trim();
13971
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
13972
+ return normalized;
13973
+ }
13974
+ function completeSurfaceForegrounds(input) {
13975
+ const text = requireSurfaceColorValue("text", input.text);
14085
13976
  return {
14086
- ...withoutLegacyText,
14087
- background: background.type === "solid" ? {
14088
- ...background,
14089
- colorToken: stripLegacyTokenAlpha(background.colorToken)
14090
- } : background,
14091
- border: normalizedBorder,
14092
- affordance: normalizedAffordance,
14093
- effects: normalizedEffects,
14094
- hoverBackgroundToken
14095
- };
14096
- }
14097
- function normalizeLegacyButtonGlobalInput(input) {
14098
- if (!isUnknownRecord(input)) return input;
14099
- const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
14100
- return hoverColorToken === input.hoverColorToken ? input : {
14101
- ...input,
14102
- hoverColorToken
13977
+ text,
13978
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
13979
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
13980
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
14103
13981
  };
14104
13982
  }
14105
- function hasLegacyAlphaToken(input, background) {
14106
- const stops = isUnknownArray(background.stops) ? background.stops : [];
14107
- return [
14108
- input.hoverBackgroundToken,
14109
- background.colorToken,
14110
- background.foregroundToken,
14111
- ...stops
14112
- ].some((token) => stripLegacyTokenAlpha(token) !== token);
14113
- }
14114
- function stripLegacyTokenAlpha(value) {
14115
- if (typeof value !== "string") return value;
14116
- const slashIndex = value.indexOf("/");
14117
- return slashIndex < 0 ? value : value.slice(0, slashIndex);
14118
- }
14119
- function hasLegacyButtonEffects(input) {
14120
- if (!isUnknownRecord(input)) return false;
14121
- return [
14122
- "base",
14123
- "hover",
14124
- "active",
14125
- "focus"
14126
- ].some((state) => {
14127
- const effects = input[state];
14128
- return Array.isArray(effects) && effects.some((effect) => {
14129
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
14130
- const classification = classifyButtonEffectId(effect.effectId);
14131
- if (classification !== "colour-transition" && classification !== "non-colour") return true;
14132
- return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
14133
- });
14134
- });
14135
- }
14136
- function normalizeLegacyButtonEffects(input, backgroundType) {
14137
- if (!isUnknownRecord(input)) return input;
14138
- const normalized = {};
14139
- for (const state of [
14140
- "base",
14141
- "hover",
14142
- "active",
14143
- "focus"
14144
- ]) {
14145
- const effects = input[state];
14146
- if (!Array.isArray(effects)) continue;
14147
- const safeEffects = effects.flatMap((effect) => {
14148
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
14149
- const classification = classifyButtonEffectId(effect.effectId);
14150
- if (classification === "non-colour") return [effect];
14151
- if (classification !== "colour-transition") return [];
14152
- if (effect.effectId === "background-on-hover") {
14153
- const options = isUnknownRecord(effect.options) ? effect.options : null;
14154
- const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
14155
- return colorToken === null ? [] : [{
14156
- ...effect,
14157
- options: {
14158
- ...options,
14159
- colorToken
14160
- }
14161
- }];
14162
- }
14163
- if (backgroundType !== "solid") return [];
14164
- const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
14165
- const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
14166
- return [{
14167
- ...effect,
14168
- options: {
14169
- ...isUnknownRecord(effect.options) ? effect.options : {},
14170
- amount
14171
- }
14172
- }];
14173
- });
14174
- if (safeEffects.length > 0) normalized[state] = safeEffects;
14175
- }
14176
- return Object.keys(normalized).length === 0 ? void 0 : normalized;
13983
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
13984
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
13985
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
13986
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
13987
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
14177
13988
  }
14178
- function isUnknownRecord(value) {
14179
- return typeof value === "object" && value !== null && !Array.isArray(value);
13989
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
13990
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
13991
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
14180
13992
  }
14181
- function isUnknownArray(value) {
14182
- return Array.isArray(value);
13993
+ function surfaceForegroundStyle(input) {
13994
+ const foregrounds = completeSurfaceForegrounds(input);
13995
+ return {
13996
+ color: foregrounds.text,
13997
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
13998
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
13999
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
14000
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
14001
+ };
14183
14002
  }
14184
14003
  buttonSizeNameSchema.options;
14185
14004
  //#endregion
@@ -14336,7 +14155,6 @@ const quietArrowRecipe = defineButtonStyleRecipe({
14336
14155
  cornerStyle: "rounded",
14337
14156
  textTransform: "uppercase",
14338
14157
  fontWeight: 500,
14339
- borderWidth: 1,
14340
14158
  typography: "body",
14341
14159
  fontSize: "text-sm",
14342
14160
  letterSpacing: "loose",
@@ -14400,7 +14218,7 @@ const quietArrowRecipe = defineButtonStyleRecipe({
14400
14218
  affordance: { iconId: "ArrowRight" },
14401
14219
  effects: { hover: [{
14402
14220
  effectId: "background-on-hover",
14403
- options: { colorToken: "surfaceAlt" }
14221
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
14404
14222
  }] }
14405
14223
  }
14406
14224
  },
@@ -14511,7 +14329,6 @@ const softPillRecipe = defineStaticButtonStyleRecipe({
14511
14329
  shadow: "low",
14512
14330
  textTransform: "none",
14513
14331
  fontWeight: 500,
14514
- borderWidth: 2,
14515
14332
  hoverTransform: "lift",
14516
14333
  hoverColor: "none"
14517
14334
  },
@@ -14530,10 +14347,7 @@ const softPillRecipe = defineStaticButtonStyleRecipe({
14530
14347
  position: "bottom"
14531
14348
  },
14532
14349
  effects: {
14533
- hover: [{
14534
- effectId: "lift",
14535
- options: { translateY: "-1px" }
14536
- }],
14350
+ hover: [{ effectId: "lift" }],
14537
14351
  active: [{
14538
14352
  effectId: "scale-down",
14539
14353
  options: { scale: "0.98" }
@@ -14552,7 +14366,7 @@ const softPillRecipe = defineStaticButtonStyleRecipe({
14552
14366
  border: { widthClass: "border-2" },
14553
14367
  effects: { hover: [{
14554
14368
  effectId: "background-on-hover",
14555
- options: { colorToken: "surface" }
14369
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
14556
14370
  }] }
14557
14371
  }
14558
14372
  },
@@ -14567,7 +14381,7 @@ const softPillRecipe = defineStaticButtonStyleRecipe({
14567
14381
  border: { widthClass: "border" },
14568
14382
  effects: { hover: [{
14569
14383
  effectId: "background-on-hover",
14570
- options: { colorToken: "surface" }
14384
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
14571
14385
  }] }
14572
14386
  }
14573
14387
  },
@@ -14581,7 +14395,7 @@ const softPillRecipe = defineStaticButtonStyleRecipe({
14581
14395
  borderRadius: "rounded-full",
14582
14396
  effects: { hover: [{
14583
14397
  effectId: "background-on-hover",
14584
- options: { colorToken: "surface" }
14398
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
14585
14399
  }] }
14586
14400
  }
14587
14401
  },
@@ -14611,7 +14425,6 @@ const earthPillRecipe = defineStaticButtonStyleRecipe({
14611
14425
  shadow: "low",
14612
14426
  textTransform: "none",
14613
14427
  fontWeight: 500,
14614
- borderWidth: 1,
14615
14428
  hoverTransform: "lift",
14616
14429
  hoverColor: "none",
14617
14430
  typography: "body",
@@ -14647,12 +14460,9 @@ const earthPillRecipe = defineStaticButtonStyleRecipe({
14647
14460
  position: "bottom"
14648
14461
  },
14649
14462
  effects: {
14650
- hover: [{
14651
- effectId: "lift",
14652
- options: { translateY: "-1px" }
14653
- }, {
14463
+ hover: [{ effectId: "lift" }, {
14654
14464
  effectId: "background-on-hover",
14655
- options: { colorToken: "accent" }
14465
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
14656
14466
  }],
14657
14467
  active: [{
14658
14468
  effectId: "scale-down",
@@ -14672,7 +14482,7 @@ const earthPillRecipe = defineStaticButtonStyleRecipe({
14672
14482
  border: { widthClass: "border" },
14673
14483
  effects: { hover: [{
14674
14484
  effectId: "background-on-hover",
14675
- options: { colorToken: "muted" }
14485
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
14676
14486
  }] }
14677
14487
  }
14678
14488
  },
@@ -14687,7 +14497,7 @@ const earthPillRecipe = defineStaticButtonStyleRecipe({
14687
14497
  border: { widthClass: "border" },
14688
14498
  effects: { hover: [{
14689
14499
  effectId: "background-on-hover",
14690
- options: { colorToken: "surfaceAlt" }
14500
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
14691
14501
  }] }
14692
14502
  }
14693
14503
  },
@@ -14747,7 +14557,6 @@ const confidentChipRecipe = defineStaticButtonStyleRecipe({
14747
14557
  shadow: "medium",
14748
14558
  textTransform: "none",
14749
14559
  fontWeight: 600,
14750
- borderWidth: 1,
14751
14560
  hoverTransform: "none",
14752
14561
  hoverColor: "none"
14753
14562
  },
@@ -14788,7 +14597,7 @@ const confidentChipRecipe = defineStaticButtonStyleRecipe({
14788
14597
  border: { widthClass: "border" },
14789
14598
  effects: { hover: [{
14790
14599
  effectId: "background-on-hover",
14791
- options: { colorToken: "background" }
14600
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
14792
14601
  }] }
14793
14602
  }
14794
14603
  },
@@ -14803,7 +14612,7 @@ const confidentChipRecipe = defineStaticButtonStyleRecipe({
14803
14612
  border: { widthClass: "border" },
14804
14613
  effects: { hover: [{
14805
14614
  effectId: "background-on-hover",
14806
- options: { colorToken: "surface" }
14615
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
14807
14616
  }] }
14808
14617
  }
14809
14618
  },
@@ -14817,7 +14626,7 @@ const confidentChipRecipe = defineStaticButtonStyleRecipe({
14817
14626
  borderRadius: "rounded-md",
14818
14627
  effects: { hover: [{
14819
14628
  effectId: "background-on-hover",
14820
- options: { colorToken: "surface" }
14629
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
14821
14630
  }] }
14822
14631
  }
14823
14632
  },
@@ -14852,7 +14661,6 @@ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
14852
14661
  cornerStyle: "square",
14853
14662
  textTransform: "none",
14854
14663
  fontWeight: 500,
14855
- borderWidth: 1,
14856
14664
  hoverTransform: "none",
14857
14665
  hoverColor: "none"
14858
14666
  },
@@ -14870,7 +14678,6 @@ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
14870
14678
  effectId: "border-bottom-grow",
14871
14679
  options: {
14872
14680
  height: 2,
14873
- colorToken: "text",
14874
14681
  origin: "left"
14875
14682
  }
14876
14683
  }] }
@@ -14889,7 +14696,6 @@ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
14889
14696
  effectId: "border-bottom-grow",
14890
14697
  options: {
14891
14698
  height: 1,
14892
- colorToken: "mutedText",
14893
14699
  origin: "left"
14894
14700
  }
14895
14701
  }] }
@@ -14908,7 +14714,6 @@ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
14908
14714
  effectId: "border-bottom-grow",
14909
14715
  options: {
14910
14716
  height: 1,
14911
- colorToken: "accent",
14912
14717
  origin: "left"
14913
14718
  }
14914
14719
  }] }
@@ -14932,7 +14737,6 @@ const editorialLinkRecipe = defineStaticButtonStyleRecipe({
14932
14737
  effectId: "border-bottom-grow",
14933
14738
  options: {
14934
14739
  height: 2,
14935
- colorToken: "accent",
14936
14740
  origin: "left"
14937
14741
  }
14938
14742
  }] } }
@@ -14954,7 +14758,6 @@ const pebbleRecipe = defineStaticButtonStyleRecipe({
14954
14758
  cornerStyle: "pill",
14955
14759
  textTransform: "none",
14956
14760
  fontWeight: 500,
14957
- borderWidth: 2,
14958
14761
  hoverTransform: "none",
14959
14762
  hoverColor: "none"
14960
14763
  },
@@ -14999,7 +14802,7 @@ const pebbleRecipe = defineStaticButtonStyleRecipe({
14999
14802
  border: { widthClass: "border-2" },
15000
14803
  effects: { hover: [{
15001
14804
  effectId: "background-on-hover",
15002
- options: { colorToken: "accent" }
14805
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
15003
14806
  }] }
15004
14807
  }
15005
14808
  },
@@ -15014,7 +14817,7 @@ const pebbleRecipe = defineStaticButtonStyleRecipe({
15014
14817
  border: { widthClass: "border" },
15015
14818
  effects: { hover: [{
15016
14819
  effectId: "background-on-hover",
15017
- options: { colorToken: "surface" }
14820
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15018
14821
  }] }
15019
14822
  }
15020
14823
  },
@@ -15069,7 +14872,6 @@ const brushedWashRecipe = defineStaticButtonStyleRecipe({
15069
14872
  shadow: "low",
15070
14873
  textTransform: "none",
15071
14874
  fontWeight: 500,
15072
- borderWidth: 1,
15073
14875
  hoverTransform: "none",
15074
14876
  hoverColor: "none"
15075
14877
  },
@@ -15134,7 +14936,7 @@ const brushedWashRecipe = defineStaticButtonStyleRecipe({
15134
14936
  border: { widthClass: "border" },
15135
14937
  effects: { hover: [{
15136
14938
  effectId: "background-on-hover",
15137
- options: { colorToken: "surface" }
14939
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15138
14940
  }] }
15139
14941
  }
15140
14942
  },
@@ -15148,7 +14950,7 @@ const brushedWashRecipe = defineStaticButtonStyleRecipe({
15148
14950
  borderRadius: "rounded-lg",
15149
14951
  effects: { hover: [{
15150
14952
  effectId: "background-on-hover",
15151
- options: { colorToken: "surface" }
14953
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15152
14954
  }] }
15153
14955
  }
15154
14956
  },
@@ -15177,7 +14979,6 @@ const inkStampRecipe = defineStaticButtonStyleRecipe({
15177
14979
  cornerStyle: "rounded",
15178
14980
  textTransform: "uppercase",
15179
14981
  fontWeight: 600,
15180
- borderWidth: 1,
15181
14982
  hoverTransform: "none",
15182
14983
  hoverColor: "none",
15183
14984
  letterSpacing: "loose"
@@ -15235,7 +15036,7 @@ const inkStampRecipe = defineStaticButtonStyleRecipe({
15235
15036
  border: { widthClass: "border-2" },
15236
15037
  effects: { hover: [{
15237
15038
  effectId: "background-on-hover",
15238
- options: { colorToken: "surface" }
15039
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15239
15040
  }] }
15240
15041
  }
15241
15042
  },
@@ -15250,7 +15051,7 @@ const inkStampRecipe = defineStaticButtonStyleRecipe({
15250
15051
  border: { widthClass: "border-2" },
15251
15052
  effects: { hover: [{
15252
15053
  effectId: "background-on-hover",
15253
- options: { colorToken: "surface" }
15054
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15254
15055
  }] }
15255
15056
  }
15256
15057
  },
@@ -15284,7 +15085,6 @@ const inkStampRecipe = defineStaticButtonStyleRecipe({
15284
15085
  effectId: "border-bottom-grow",
15285
15086
  options: {
15286
15087
  height: 1,
15287
- colorToken: "accent",
15288
15088
  origin: "left"
15289
15089
  }
15290
15090
  }] }
@@ -15315,7 +15115,6 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15315
15115
  cornerStyle: "rounded",
15316
15116
  textTransform: "none",
15317
15117
  fontWeight: 600,
15318
- borderWidth: 1,
15319
15118
  typography: "body",
15320
15119
  letterSpacing: "tight",
15321
15120
  paddingPreset: "default",
@@ -15347,12 +15146,9 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15347
15146
  },
15348
15147
  borderRadius: "rounded-lg",
15349
15148
  affordance: { iconId: "ArrowRight" },
15350
- effects: { hover: [{
15351
- effectId: "lift",
15352
- options: { translateY: "-2px" }
15353
- }, {
15149
+ effects: { hover: [{ effectId: "lift" }, {
15354
15150
  effectId: "background-on-hover",
15355
- options: { colorToken: "secondary" }
15151
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("secondary") }
15356
15152
  }] }
15357
15153
  }
15358
15154
  },
@@ -15365,12 +15161,9 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15365
15161
  },
15366
15162
  borderRadius: "rounded-lg",
15367
15163
  border: { widthClass: "border" },
15368
- effects: { hover: [{
15369
- effectId: "lift",
15370
- options: { translateY: "-2px" }
15371
- }, {
15164
+ effects: { hover: [{ effectId: "lift" }, {
15372
15165
  effectId: "background-on-hover",
15373
- options: { colorToken: "surfaceAlt" }
15166
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
15374
15167
  }] }
15375
15168
  }
15376
15169
  },
@@ -15383,12 +15176,9 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15383
15176
  },
15384
15177
  borderRadius: "rounded-lg",
15385
15178
  border: { widthClass: "border" },
15386
- effects: { hover: [{
15387
- effectId: "lift",
15388
- options: { translateY: "-2px" }
15389
- }, {
15179
+ effects: { hover: [{ effectId: "lift" }, {
15390
15180
  effectId: "background-on-hover",
15391
- options: { colorToken: "surfaceAlt" }
15181
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
15392
15182
  }] }
15393
15183
  }
15394
15184
  },
@@ -15400,10 +15190,7 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15400
15190
  role: "link"
15401
15191
  },
15402
15192
  borderRadius: "rounded-lg",
15403
- effects: { hover: [{
15404
- effectId: "lift",
15405
- options: { translateY: "-2px" }
15406
- }] }
15193
+ effects: { hover: [{ effectId: "lift" }] }
15407
15194
  }
15408
15195
  },
15409
15196
  accent: {
@@ -15415,10 +15202,7 @@ const flatInkRecipe = defineStaticButtonStyleRecipe({
15415
15202
  },
15416
15203
  borderRadius: "rounded-lg",
15417
15204
  affordance: { iconId: "ArrowRight" },
15418
- effects: { hover: [{
15419
- effectId: "lift",
15420
- options: { translateY: "-2px" }
15421
- }] }
15205
+ effects: { hover: [{ effectId: "lift" }] }
15422
15206
  }
15423
15207
  }
15424
15208
  }
@@ -15439,9 +15223,8 @@ const showtimePillRecipe = defineStaticButtonStyleRecipe({
15439
15223
  shadow: "low",
15440
15224
  textTransform: "uppercase",
15441
15225
  fontWeight: 700,
15442
- borderWidth: 2,
15443
15226
  hoverTransform: "lift",
15444
- hoverColor: "darken",
15227
+ hoverColor: "none",
15445
15228
  typography: "body",
15446
15229
  paddingPreset: "spacious",
15447
15230
  italic: false
@@ -15499,7 +15282,7 @@ const showtimePillRecipe = defineStaticButtonStyleRecipe({
15499
15282
  border: { widthClass: "border-2" },
15500
15283
  effects: { hover: [{
15501
15284
  effectId: "background-on-hover",
15502
- options: { colorToken: "background" }
15285
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
15503
15286
  }] }
15504
15287
  }
15505
15288
  },
@@ -15513,7 +15296,7 @@ const showtimePillRecipe = defineStaticButtonStyleRecipe({
15513
15296
  borderRadius: "rounded-full",
15514
15297
  effects: { hover: [{
15515
15298
  effectId: "background-on-hover",
15516
- options: { colorToken: "surface" }
15299
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15517
15300
  }] }
15518
15301
  }
15519
15302
  },
@@ -15548,7 +15331,6 @@ const joyStampRecipe = defineStaticButtonStyleRecipe({
15548
15331
  typography: "heading",
15549
15332
  fontWeight: 700,
15550
15333
  fontSize: "text-lg",
15551
- borderWidth: 2,
15552
15334
  hoverTransform: "none",
15553
15335
  hoverColor: "none"
15554
15336
  },
@@ -15583,7 +15365,7 @@ const joyStampRecipe = defineStaticButtonStyleRecipe({
15583
15365
  elevation: "high",
15584
15366
  softness: "hard",
15585
15367
  position: "bottom-right",
15586
- colorToken: "text"
15368
+ colorToken: unsafeAsOpaqueButtonColourToken("text")
15587
15369
  },
15588
15370
  effects: { hover: [{ effectId: "press-down" }] }
15589
15371
  }
@@ -15601,7 +15383,7 @@ const joyStampRecipe = defineStaticButtonStyleRecipe({
15601
15383
  elevation: "high",
15602
15384
  softness: "hard",
15603
15385
  position: "bottom-right",
15604
- colorToken: "secondary"
15386
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
15605
15387
  },
15606
15388
  effects: { hover: [{ effectId: "press-down" }] }
15607
15389
  }
@@ -15646,7 +15428,6 @@ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
15646
15428
  cornerStyle: "rounded",
15647
15429
  textTransform: "uppercase",
15648
15430
  fontWeight: 600,
15649
- borderWidth: 1,
15650
15431
  hoverTransform: "none",
15651
15432
  hoverColor: "none",
15652
15433
  letterSpacing: "loose"
@@ -15682,7 +15463,7 @@ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
15682
15463
  effects: {
15683
15464
  hover: [{
15684
15465
  effectId: "background-on-hover",
15685
- options: { colorToken: "primary-100" }
15466
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("primary-100") }
15686
15467
  }],
15687
15468
  active: [{
15688
15469
  effectId: "scale-down",
@@ -15702,7 +15483,7 @@ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
15702
15483
  border: { widthClass: "border" },
15703
15484
  effects: { hover: [{
15704
15485
  effectId: "background-on-hover",
15705
- options: { colorToken: "surface" }
15486
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
15706
15487
  }] }
15707
15488
  }
15708
15489
  },
@@ -15717,7 +15498,7 @@ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
15717
15498
  border: { widthClass: "border" },
15718
15499
  effects: { hover: [{
15719
15500
  effectId: "background-on-hover",
15720
- options: { colorToken: "muted" }
15501
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
15721
15502
  }] }
15722
15503
  }
15723
15504
  },
@@ -15751,7 +15532,6 @@ const ornateRitualRecipe = defineStaticButtonStyleRecipe({
15751
15532
  effectId: "border-bottom-grow",
15752
15533
  options: {
15753
15534
  height: 1,
15754
- colorToken: "accent",
15755
15535
  origin: "left"
15756
15536
  }
15757
15537
  }] }
@@ -16860,6 +16640,102 @@ new Set(themeV2NormalEditorRawStyleFieldNames);
16860
16640
  const pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
16861
16641
  const siteChromeBackgroundOverrideOwner = "site-chrome-editor";
16862
16642
  //#endregion
16643
+ //#region ../theme-core/src/utils/colorStyles.ts
16644
+ const COLOR_VAR_PREFIX = "--tb-";
16645
+ function parseToken(source) {
16646
+ const parts = parseColorTokenParts(source);
16647
+ if (parts.kind === "raw") return {
16648
+ token: source,
16649
+ raw: parts.value
16650
+ };
16651
+ if (parts.kind === "invalid") return { token: source };
16652
+ return {
16653
+ token: parts.token,
16654
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
16655
+ };
16656
+ }
16657
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
16658
+ text: "text",
16659
+ heading: "heading",
16660
+ mutedText: "supporting"
16661
+ };
16662
+ function surfaceRoleForTextToken(token) {
16663
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
16664
+ }
16665
+ /**
16666
+ * Converts a theme color token to a CSS rgb value.
16667
+ * This is the primary function for applying theme colors to CSS properties.
16668
+ *
16669
+ * @param token - Theme color token (semantic, with opacity, or raw)
16670
+ * @returns CSS color value (rgb with optional alpha, or raw CSS value)
16671
+ *
16672
+ * @example
16673
+ * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
16674
+ * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
16675
+ * rgbColorValue('raw:transparent') // => 'transparent'
16676
+ */
16677
+ function rgbColorValue(token) {
16678
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
16679
+ if (raw) return raw;
16680
+ const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
16681
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
16682
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
16683
+ }
16684
+ /**
16685
+ * Creates a backgroundColor style object from a theme token.
16686
+ *
16687
+ * @param token - Theme color token
16688
+ * @returns CSSProperties with backgroundColor
16689
+ */
16690
+ function backgroundColorStyle(token) {
16691
+ return { backgroundColor: rgbColorValue(token) };
16692
+ }
16693
+ /**
16694
+ * Creates a text color style object from a theme token.
16695
+ *
16696
+ * @param token - Theme color token
16697
+ * @returns CSSProperties with color
16698
+ */
16699
+ function textColorStyle(token) {
16700
+ const parsed = parseToken(token);
16701
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
16702
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
16703
+ return { color: rgbColorValue(token) };
16704
+ }
16705
+ /**
16706
+ * Creates a border color style object from a theme token.
16707
+ *
16708
+ * @param token - Theme color token
16709
+ * @returns CSSProperties with borderColor
16710
+ */
16711
+ function borderColorStyle(token) {
16712
+ return { borderColor: rgbColorValue(token) };
16713
+ }
16714
+ /**
16715
+ * Merges multiple CSSProperties objects into a single object.
16716
+ * Later styles override earlier ones. Undefined values are filtered out.
16717
+ * Returns undefined if the merged result is empty.
16718
+ *
16719
+ * @param styles - Variable number of CSSProperties objects (can be undefined)
16720
+ * @returns Merged CSSProperties object, or undefined if empty
16721
+ *
16722
+ * @example
16723
+ * mergeStyles(
16724
+ * backgroundColorStyle('surface'),
16725
+ * textColorStyle('text'),
16726
+ * borderColorStyle('border')
16727
+ * )
16728
+ * // => { backgroundColor: '...', color: '...', borderColor: '...' }
16729
+ */
16730
+ function mergeStyles(...styles) {
16731
+ const merged = {};
16732
+ for (const style of styles) {
16733
+ if (!style) continue;
16734
+ Object.assign(merged, style);
16735
+ }
16736
+ return Object.keys(merged).length ? merged : void 0;
16737
+ }
16738
+ //#endregion
16863
16739
  //#region ../theme-core/src/utils/tokenOrColor.ts
16864
16740
  /**
16865
16741
  * Runtime color-reference fields intentionally accept any string, including
@@ -23741,6 +23617,7 @@ const DISPLAY_MEDIA_KINDS = [
23741
23617
  "video",
23742
23618
  "audio"
23743
23619
  ];
23620
+ const DEFAULT_MEDIA_FIELD_KINDS = ["image"];
23744
23621
  const PUBLIC_FILE_FIELD_KINDS = [
23745
23622
  "document",
23746
23623
  "spreadsheet",
@@ -23941,18 +23818,28 @@ const inlineTextFieldSchema = baseFieldSchema.extend({
23941
23818
  maxLength: z$1.number().int().positive().optional(),
23942
23819
  plainTextField: z$1.string().min(1).optional()
23943
23820
  });
23821
+ function hasMediaDefaultIdentifier(value) {
23822
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
23823
+ }
23824
+ function validateMediaFieldDefault(field, ctx) {
23825
+ if (field.defaultValue !== void 0 && field.defaultValue !== null && !hasMediaDefaultIdentifier(field.defaultValue)) ctx.addIssue({
23826
+ code: z$1.ZodIssueCode.custom,
23827
+ path: ["defaultValue"],
23828
+ message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
23829
+ });
23830
+ }
23944
23831
  const mediaFieldSchema = baseFieldSchema.extend({
23945
23832
  type: z$1.literal("media"),
23946
- mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default(["image"]).transform((kinds) => {
23833
+ mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default([...DEFAULT_MEDIA_FIELD_KINDS]).transform((kinds) => {
23947
23834
  const displayKinds = kinds.filter(isDisplayMediaKind);
23948
- return displayKinds.length > 0 ? displayKinds : ["image"];
23835
+ return displayKinds.length > 0 ? displayKinds : [...DEFAULT_MEDIA_FIELD_KINDS];
23949
23836
  }),
23950
23837
  aspectRatio: z$1.string().optional()
23951
- });
23838
+ }).superRefine(validateMediaFieldDefault);
23952
23839
  const fileFieldSchema = baseFieldSchema.extend({
23953
23840
  type: z$1.literal("file"),
23954
23841
  fileKinds: z$1.array(z$1.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
23955
- });
23842
+ }).superRefine(validateMediaFieldDefault);
23956
23843
  const booleanFieldSchema = baseFieldSchema.extend({ type: z$1.literal("boolean") });
23957
23844
  const dateFieldSchema = baseFieldSchema.extend({ type: z$1.literal("date") });
23958
23845
  const timeFieldSchema = baseFieldSchema.extend({ type: z$1.literal("time") });
@@ -32256,20 +32143,10 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
32256
32143
  }
32257
32144
  function interpolateRichTextTokens(value, options, context) {
32258
32145
  if (!isRecord$11(value)) return value;
32259
- const allowedTokens = new Set(options.tokens);
32260
- const tokenValues = {
32146
+ return visitRichTextNode(value, new Set(options.tokens), {
32261
32147
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
32262
32148
  site_name: getSiteName(context)
32263
- };
32264
- if (isRecord$11(value.doc)) {
32265
- const nextDoc = visitRichTextNode(value.doc, allowedTokens, tokenValues);
32266
- if (nextDoc === value.doc) return value;
32267
- return {
32268
- ...value,
32269
- doc: nextDoc
32270
- };
32271
- }
32272
- return visitRichTextNode(value, allowedTokens, tokenValues);
32149
+ });
32273
32150
  }
32274
32151
  const contentTransforms = [{
32275
32152
  id: "content.interpolateRichTextTokens",
@@ -34652,11 +34529,8 @@ const mediaSchema = z.discriminatedUnion("type", [
34652
34529
  * This module provides a single source of truth for TipTap rich text validation
34653
34530
  * across both the block-form editor and the block rendering system.
34654
34531
  *
34655
- * Format compatibility:
34656
- * - Unwrapped format (new): { type: 'doc', content: [...] }
34657
- * - Wrapped format (legacy): { doc: { type: 'doc', content: [...] } }
34658
- *
34659
- * Both formats are supported for backward compatibility during migration.
34532
+ * Rich-text fields use one canonical root shape:
34533
+ * `{ type: 'doc', content: [...] }`.
34660
34534
  */
34661
34535
  /**
34662
34536
  * TipTap document node schema (recursive structure)
@@ -34669,17 +34543,40 @@ const mediaSchema = z.discriminatedUnion("type", [
34669
34543
  * - marks: Text formatting marks (e.g., bold, italic)
34670
34544
  */
34671
34545
  const TipTapMarkSchema = z$1.object({
34672
- type: z$1.string(),
34546
+ type: z$1.string().min(1),
34673
34547
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional()
34674
- });
34548
+ }).strict();
34675
34549
  const TipTapNodeSchema = z$1.lazy(() => z$1.object({
34676
- type: z$1.string(),
34550
+ type: z$1.string().min(1),
34677
34551
  content: z$1.array(TipTapNodeSchema).optional(),
34678
34552
  text: z$1.string().optional(),
34679
34553
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional(),
34680
34554
  marks: z$1.array(TipTapMarkSchema).optional()
34555
+ }).strict().superRefine((node, ctx) => {
34556
+ if (node.type === "doc") ctx.addIssue({
34557
+ code: "custom",
34558
+ path: ["type"],
34559
+ message: "Document nodes are only valid at the rich-text root"
34560
+ });
34561
+ if (node.type === "text") {
34562
+ if (node.text === void 0 || node.text.length === 0) ctx.addIssue({
34563
+ code: "custom",
34564
+ path: ["text"],
34565
+ message: "Text nodes require a non-empty text value"
34566
+ });
34567
+ if (node.content !== void 0) ctx.addIssue({
34568
+ code: "custom",
34569
+ path: ["content"],
34570
+ message: "Text nodes cannot contain child nodes"
34571
+ });
34572
+ return;
34573
+ }
34574
+ if (node.text !== void 0) ctx.addIssue({
34575
+ code: "custom",
34576
+ path: ["text"],
34577
+ message: "Only text nodes can contain a text value"
34578
+ });
34681
34579
  }));
34682
- z$1.union([TipTapNodeSchema, z$1.object({ doc: TipTapNodeSchema })]);
34683
34580
  /**
34684
34581
  * Canonical schema for a TipTap rich-text document root.
34685
34582
  *
@@ -34688,40 +34585,20 @@ z$1.union([TipTapNodeSchema, z$1.object({ doc: TipTapNodeSchema })]);
34688
34585
  * (dummy content, editor output, migrations) must emit, and the shape every
34689
34586
  * non-legacy consumer can rely on.
34690
34587
  *
34691
- * Legacy envelopes are read-tolerated by {@link RichTextValueSchema} but
34692
- * normalised before reaching anywhere typed as `RichTextDoc`.
34693
34588
  */
34694
34589
  const RichTextDocSchema = z$1.object({
34695
34590
  type: z$1.literal("doc"),
34696
34591
  content: z$1.array(TipTapNodeSchema)
34697
- });
34592
+ }).strict();
34698
34593
  /**
34699
- * Permissive unwrap for a value known to be the root of a rich-text field.
34700
- *
34701
- * Producers should emit the canonical unwrapped form `{ type: 'doc', content: [...] }`,
34702
- * but two legacy shapes still appear in older data and pre-Phase-1 fixtures:
34703
- *
34704
- * - `{ doc: <typed-doc> }` — pre-migration DB storage.
34705
- * - `{ doc: <typed-doc>, html, plain }` — pre-Phase-1 dummy-content output.
34594
+ * Type guard for {@link RichTextDoc}.
34706
34595
  *
34707
- * This helper is the single right place to tolerate either shape at a known
34708
- * rich-text root (transforms, coercion, validators that operate on rich text).
34709
- * The strictness sits on the inner node's `type === 'doc'`, which prevents
34710
- * misidentifying random objects that happen to carry a field named `doc`.
34711
- *
34712
- * Returns the inner doc node when the input matches a legacy shape, otherwise
34713
- * returns the input unchanged.
34714
- *
34715
- * For deep structural traversal where any nested key might be named `doc`
34716
- * (and unwrapping would be incorrect), use the strict `isDocWrapper` private
34717
- * helper inside `@riverbankcms/blocks/bindings`.
34596
+ * Returns `true` only for the canonical unwrapped form. The shared structural
34597
+ * contract lives in `@riverbankcms/core`; parity tests keep its dependency-free
34598
+ * guard aligned with this detailed Zod validator.
34718
34599
  */
34719
- function unwrapRichTextRoot(value) {
34720
- if (!isRecord$11(value)) return value;
34721
- if (!("doc" in value)) return value;
34722
- const inner = value.doc;
34723
- if (isRecord$11(inner) && inner.type === "doc") return inner;
34724
- return value;
34600
+ function isRichTextDoc(value) {
34601
+ return RichTextDocSchema.safeParse(value).success;
34725
34602
  }
34726
34603
  //#endregion
34727
34604
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
@@ -46087,6 +45964,7 @@ const CANONICAL_REFUND_PLANNER_ERROR_CODES = [
46087
45964
  "idempotency_amount_mismatch",
46088
45965
  "idempotency_method_mismatch",
46089
45966
  "idempotency_receipt_mismatch",
45967
+ "idempotency_scope_mismatch",
46090
45968
  "refund_in_progress"
46091
45969
  ],
46092
45970
  "invalid_amount",
@@ -46473,7 +46351,7 @@ const SITE_BANNER_TONE_VALUES = [
46473
46351
  ];
46474
46352
  const siteBannerScopeSchema = z$1.enum(SITE_BANNER_SCOPE_VALUES);
46475
46353
  const siteBannerToneSchema = z$1.enum(SITE_BANNER_TONE_VALUES);
46476
- const siteBannerBodySchema = z$1.unknown().transform(unwrapRichTextValue);
46354
+ const siteBannerBodySchema = RichTextDocSchema;
46477
46355
  const siteBannerCtaSchema = z$1.object({
46478
46356
  label: z$1.string().trim().min(1),
46479
46357
  link: linkPayloadSchema
@@ -46501,7 +46379,8 @@ const EMPTY_SITE_BANNER_BODY = {
46501
46379
  }]
46502
46380
  };
46503
46381
  function analyzeSiteBannerBody(body) {
46504
- const sanitizedBody = sanitizeSiteBannerBodyInternal(body, { limitParagraphs: false });
46382
+ const parsed = RichTextDocSchema.safeParse(body);
46383
+ const sanitizedBody = parsed.success ? sanitizeSiteBannerBodyInternal(parsed.data, { limitParagraphs: false }) : EMPTY_SITE_BANNER_BODY;
46505
46384
  const paragraphs = collectParagraphs(sanitizedBody);
46506
46385
  const textLength = paragraphs.reduce((total, paragraph) => total + paragraphTextLength(paragraph), 0);
46507
46386
  const issues = [];
@@ -46523,7 +46402,7 @@ function siteBannerBodyIssueMessage(issue) {
46523
46402
  }
46524
46403
  }
46525
46404
  function sanitizeSiteBannerBodyInternal(input, options) {
46526
- const content = normalizeTopLevelNodes(unwrapRichTextValue(input).content ?? [], options);
46405
+ const content = normalizeTopLevelNodes(input.content, options);
46527
46406
  return {
46528
46407
  type: "doc",
46529
46408
  content: content.length > 0 ? content : EMPTY_SITE_BANNER_BODY.content
@@ -46604,28 +46483,6 @@ function sanitizeMarks(marks) {
46604
46483
  });
46605
46484
  return sanitized.length > 0 ? sanitized : void 0;
46606
46485
  }
46607
- function unwrapRichTextValue(value) {
46608
- if (isRecord$3(value) && isRecord$3(value.doc)) return unwrapRichTextValue(value.doc);
46609
- if (isRecord$3(value) && value.type === "doc") return {
46610
- type: "doc",
46611
- content: Array.isArray(value.content) ? value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) : []
46612
- };
46613
- return EMPTY_SITE_BANNER_BODY;
46614
- }
46615
- function isTipTapNodeLike(value) {
46616
- return isRecord$3(value) && typeof value.type === "string";
46617
- }
46618
- function coerceTipTapNode(value) {
46619
- return {
46620
- type: value.type,
46621
- ...Array.isArray(value.content) ? { content: value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) } : {},
46622
- ...typeof value.text === "string" ? { text: value.text } : {},
46623
- ...Array.isArray(value.marks) ? { marks: value.marks.filter((mark) => isRecord$3(mark) && typeof mark.type === "string").map((mark) => ({
46624
- type: mark.type,
46625
- ...isRecord$3(mark.attrs) ? { attrs: mark.attrs } : {}
46626
- })) } : {}
46627
- };
46628
- }
46629
46486
  function paragraphTextLength(paragraph) {
46630
46487
  return collectText(paragraph).length;
46631
46488
  }
@@ -47584,20 +47441,14 @@ function textToInlineText(value) {
47584
47441
  /**
47585
47442
  * Extracts the first paragraph text from a TipTap/ProseMirror document.
47586
47443
  *
47587
- * Accepts either the canonical unwrapped form (`{ type: 'doc', content: [...] }`)
47588
- * or the legacy envelope (`{ doc: { type: 'doc', content: [...] } }`). New
47589
- * producers must emit the unwrapped form; this tolerance only exists to read
47590
- * data that pre-dates the unwrap migration.
47444
+ * Accepts the canonical unwrapped form (`{ type: 'doc', content: [...] }`).
47591
47445
  */
47592
47446
  function extractFirstParagraph(value) {
47593
- const unwrapped = unwrapRichTextRoot(value);
47594
- if (!isRecord$11(unwrapped)) return null;
47595
- const doc = unwrapped;
47596
- if (!Array.isArray(doc.content)) return null;
47597
- for (const node of doc.content) {
47447
+ if (!isRichTextDoc(value)) return null;
47448
+ for (const node of value.content) {
47598
47449
  if (!isRecord$11(node)) continue;
47599
47450
  if (node.type !== "paragraph" || !Array.isArray(node.content)) continue;
47600
- const text = node.content.filter((item) => isRecord$11(item) && item.type === "text").map((item) => typeof item.text === "string" ? item.text : "").join("").trim();
47451
+ const text = node.content.filter((item) => item.type === "text").map((item) => typeof item.text === "string" ? item.text : "").join("").trim();
47601
47452
  if (text.length > 0) return text;
47602
47453
  }
47603
47454
  return null;
@@ -47646,40 +47497,6 @@ function normalizeBindingInput(value) {
47646
47497
  };
47647
47498
  return null;
47648
47499
  }
47649
- /**
47650
- * Normalizes binding output by unwrapping doc wrappers.
47651
- * Rich text fields are sometimes wrapped as { doc: { type: 'doc', content: [...] } }
47652
- * This unwraps them to just the doc content.
47653
- */
47654
- function normalizeBindingOutput$1(value) {
47655
- return normalizeValue(value);
47656
- }
47657
- function normalizeValue(value) {
47658
- if (Array.isArray(value)) return value.map((item) => normalizeValue(item));
47659
- if (!isRecord$11(value)) return value;
47660
- if (isDocWrapper(value)) return normalizeValue(value.doc);
47661
- const entries = Object.entries(value).map(([key, val]) => [key, normalizeValue(val)]);
47662
- return Object.fromEntries(entries);
47663
- }
47664
- /**
47665
- * Strict doc-wrapper detector for recursive structure traversal.
47666
- *
47667
- * Matches ONLY the exact `{ doc: <typed-doc> }` shape with no sibling keys.
47668
- * The strictness is deliberate: `normalizeBindingOutput` (this file) walks
47669
- * every key of every nested object, and a permissive rule would corrupt
47670
- * legitimate structures that happen to carry a field named `doc` (e.g.
47671
- * `{ products: [{ doc: <node>, name: 'Widget' }] }`).
47672
- *
47673
- * For the *root* of a rich-text value — where we know any `doc` envelope
47674
- * is rich-text bookkeeping rather than content — use `unwrapRichTextRoot`
47675
- * (exported from `@riverbankcms/blocks/system/manifest/richTextSchema`).
47676
- */
47677
- function isDocWrapper(value) {
47678
- if (!("doc" in value)) return false;
47679
- if (Object.keys(value).length !== 1) return false;
47680
- const doc = value.doc;
47681
- return isRecord$11(doc) && typeof doc.type === "string" && doc.type === "doc";
47682
- }
47683
47500
  BINDING_TRANSFORM_VALUES$1.join(", ");
47684
47501
  //#endregion
47685
47502
  //#region src/rendering/helpers/bindings.ts
@@ -47691,7 +47508,6 @@ BINDING_TRANSFORM_VALUES$1.join(", ");
47691
47508
  * to install `@riverbankcms/blocks`.
47692
47509
  */
47693
47510
  const isRecord = isRecord$11;
47694
- const normalizeBindingOutput = normalizeBindingOutput$1;
47695
47511
  const applyBindingsToContent = applyBindingsToContent$1;
47696
47512
  //#endregion
47697
47513
  //#region ../content-model/src/entry-slots/types.ts
@@ -48155,7 +47971,7 @@ async function processTemplatePage(input) {
48155
47971
  * - Entry-scoped blocks preserve defaults and resolve bindings over them
48156
47972
  */
48157
47973
  function resolveTemplateBlock(block, bindingContext) {
48158
- const baseContent = normalizeBindingOutput(sanitizeSdkRuntimeBlockContent(block.blockKind, block.content ?? {}));
47974
+ const baseContent = sanitizeSdkRuntimeBlockContent(block.blockKind, block.content ?? {});
48159
47975
  if (block.scope === "template") return {
48160
47976
  id: block.id,
48161
47977
  kind: block.blockKind,
@@ -48163,7 +47979,7 @@ function resolveTemplateBlock(block, bindingContext) {
48163
47979
  content: baseContent
48164
47980
  };
48165
47981
  const bindings = block.bindings ?? {};
48166
- const resolvedContent = typeof bindings === "object" && Object.keys(bindings).length > 0 ? normalizeBindingOutput(applyBindingsToContent(baseContent, bindings, bindingContext)) : baseContent;
47982
+ const resolvedContent = typeof bindings === "object" && Object.keys(bindings).length > 0 ? applyBindingsToContent(baseContent, bindings, bindingContext) : baseContent;
48167
47983
  return {
48168
47984
  id: block.id,
48169
47985
  kind: block.blockKind,
@@ -48207,8 +48023,8 @@ function normalizeNullableEntrySlotContent(content) {
48207
48023
  return content == null ? null : normalizeEntrySlotBlockContent(content);
48208
48024
  }
48209
48025
  function normalizeEntrySlotBlockContent(content) {
48210
- const normalized = normalizeBindingOutput(content ?? {});
48211
- return isRecord(normalized) ? normalized : {};
48026
+ const value = content ?? {};
48027
+ return isRecord(value) ? value : {};
48212
48028
  }
48213
48029
  //#endregion
48214
48030
  export { createRiverbankClient, isEntryContent, isPageContent, isPageSurfaceContent, loadContent, loadPage };