@revenuecat/purchases-ui-js 4.7.5 → 4.8.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.
@@ -175,9 +175,6 @@
175
175
  action: {
176
176
  type: "workflow",
177
177
  },
178
- triggers: {
179
- on_press: "workflow-action-id",
180
- },
181
178
  stack: {
182
179
  ...stackProps(5),
183
180
  background_color: {
@@ -67,12 +67,6 @@
67
67
  componentName: props.name,
68
68
  componentValue: "navigate_back",
69
69
  };
70
- case "navigate_to_page":
71
- return {
72
- componentType: "button",
73
- componentName: props.name,
74
- componentValue: "navigate_to_page",
75
- };
76
70
  case "close_workflow":
77
71
  return {
78
72
  componentType: "button",
@@ -149,7 +143,9 @@
149
143
 
150
144
  const onclick = () => {
151
145
  if (isDisabled) return;
152
- const actionId = props.triggers?.on_press;
146
+ // For "workflow" actions, pass the component's own id so Workflow.svelte
147
+ // can look it up in the step's triggers array (component ID → action ID).
148
+ const actionId = action.type === "workflow" ? props.id : undefined;
153
149
  if (action.type === "navigate_to" && action.destination === "sheet") {
154
150
  if (action.sheet != null) {
155
151
  emitComponentInteraction(getSheetOpenInteractionData(action));
@@ -173,8 +169,6 @@
173
169
  return false;
174
170
  case "navigate_back":
175
171
  return !hideBackButtons;
176
- case "navigate_to_page":
177
- return true;
178
172
  case "close_workflow":
179
173
  return true;
180
174
  case "navigate_to":
@@ -25,6 +25,7 @@
25
25
  import { waitForAnimations } from "storybook/internal/preview-api";
26
26
  import { viewportDecorator } from "../../stories/viewport-decorator";
27
27
  import { BACKGROUND_PAYWALL } from "./fixtures/background-paywall";
28
+ import { FIXED_HEIGHT_STACK_PAYWALL } from "./fixtures/fixed-height-stack-paywall";
28
29
  import { OVERRIDE_PAYWALL } from "./fixtures/override-paywall";
29
30
  import { SHEET_PAYWALL } from "./fixtures/sheet-paywall";
30
31
  import { SHEET_PAYWALL_VIDEO_STACKING } from "./fixtures/sheet-video-stacking-paywall";
@@ -74,6 +75,20 @@
74
75
  }}
75
76
  />
76
77
 
78
+ <!--
79
+ Regression story for fixed-height stacks being squeezed by the flex algorithm.
80
+ The root stack uses `space_between` and children's fixed heights sum to more than
81
+ the 400px viewport, so the empty spacer would collapse without `flex-shrink: 0`.
82
+ Chromatic diffs this story to block regressions of Stack.svelte's flex-shrink rule.
83
+ -->
84
+ <Story
85
+ name="Fixed-height stack (regression)"
86
+ decorators={[viewportDecorator(320, 400, 0)]}
87
+ args={{
88
+ paywallData: FIXED_HEIGHT_STACK_PAYWALL,
89
+ }}
90
+ />
91
+
77
92
  <Story
78
93
  name="Override paywall"
79
94
  args={{
@@ -71,11 +71,6 @@
71
71
  preferredColorMode?: ColorMode;
72
72
  onPurchaseClicked?: (selectedPackageId: string, actionId: string) => void;
73
73
  onBackClicked?: () => void;
74
- /**
75
- * Called when a `navigate_to_page` button is pressed and there is no
76
- * active nav_host context to handle it internally.
77
- */
78
- onNavigateToPage?: (pageId: string) => void;
79
74
  /** Called when a `close_workflow` button is pressed to dismiss the paywall/workflow. */
80
75
  onClose?: () => void;
81
76
  onVisitCustomerCenterClicked?: () => void;
@@ -135,7 +130,6 @@
135
130
  preferredColorMode,
136
131
  onPurchaseClicked,
137
132
  onBackClicked,
138
- onNavigateToPage,
139
133
  onClose,
140
134
  onVisitCustomerCenterClicked,
141
135
  onRestorePurchasesClicked,
@@ -248,9 +242,6 @@
248
242
  case "navigate_back":
249
243
  onBackClicked?.();
250
244
  return;
251
- case "navigate_to_page":
252
- onNavigateToPage?.(action.page_id);
253
- return;
254
245
  case "close_workflow":
255
246
  onClose?.();
256
247
  return;
@@ -28,11 +28,6 @@ interface Props {
28
28
  preferredColorMode?: ColorMode;
29
29
  onPurchaseClicked?: (selectedPackageId: string, actionId: string) => void;
30
30
  onBackClicked?: () => void;
31
- /**
32
- * Called when a `navigate_to_page` button is pressed and there is no
33
- * active nav_host context to handle it internally.
34
- */
35
- onNavigateToPage?: (pageId: string) => void;
36
31
  /** Called when a `close_workflow` button is pressed to dismiss the paywall/workflow. */
37
32
  onClose?: () => void;
38
33
  onVisitCustomerCenterClicked?: () => void;
@@ -0,0 +1,2 @@
1
+ import type { PaywallData } from "../../../types/paywall";
2
+ export declare const FIXED_HEIGHT_STACK_PAYWALL: PaywallData;
@@ -0,0 +1,87 @@
1
+ // Regression fixture: fixed-height empty stack collapses under flex pressure.
2
+ // Root `space_between` with children summing above viewport height. Pair with
3
+ // `viewportDecorator` in the story so the parent is bounded; otherwise the
4
+ // paywall grows to content height and no shrinkage happens.
5
+ const solidStack = (id, height, hex) => ({
6
+ type: "stack",
7
+ id,
8
+ name: id,
9
+ components: [],
10
+ dimension: {
11
+ type: "vertical",
12
+ alignment: "center",
13
+ distribution: "center",
14
+ },
15
+ size: {
16
+ width: { type: "fill" },
17
+ height: { type: "fixed", value: height },
18
+ },
19
+ background: null,
20
+ background_color: { light: { type: "hex", value: hex } },
21
+ badge: null,
22
+ border: null,
23
+ margin: { bottom: 0, leading: 0, top: 0, trailing: 0 },
24
+ padding: { bottom: 0, leading: 0, top: 0, trailing: 0 },
25
+ shadow: null,
26
+ shape: {
27
+ type: "rectangle",
28
+ corners: {
29
+ bottom_leading: 0,
30
+ bottom_trailing: 0,
31
+ top_leading: 0,
32
+ top_trailing: 0,
33
+ },
34
+ },
35
+ spacing: 0,
36
+ });
37
+ export const FIXED_HEIGHT_STACK_PAYWALL = {
38
+ id: "fixed_height_stack_paywall",
39
+ default_locale: "en_US",
40
+ components_localizations: { en_US: {} },
41
+ components_config: {
42
+ base: {
43
+ background: {
44
+ type: "color",
45
+ value: { light: { type: "hex", value: "#ffffff" } },
46
+ },
47
+ stack: {
48
+ type: "stack",
49
+ id: "root",
50
+ name: "Root",
51
+ components: [
52
+ solidStack("hero", 180, "#ff5f5f"),
53
+ solidStack("empty_spacer", 50, "#11d483"),
54
+ solidStack("card", 200, "#6366f1"),
55
+ solidStack("cta", 48, "#000000"),
56
+ ],
57
+ dimension: {
58
+ type: "vertical",
59
+ alignment: "center",
60
+ distribution: "space_between",
61
+ },
62
+ size: {
63
+ width: { type: "fill" },
64
+ height: { type: "fill" },
65
+ },
66
+ background: null,
67
+ background_color: null,
68
+ badge: null,
69
+ border: null,
70
+ margin: { bottom: 0, leading: 0, top: 0, trailing: 0 },
71
+ padding: { bottom: 16, leading: 16, top: 16, trailing: 16 },
72
+ shadow: null,
73
+ shape: {
74
+ type: "rectangle",
75
+ corners: {
76
+ bottom_leading: 0,
77
+ bottom_trailing: 0,
78
+ top_leading: 0,
79
+ top_trailing: 0,
80
+ },
81
+ },
82
+ spacing: 12,
83
+ },
84
+ sticky_footer: null,
85
+ },
86
+ },
87
+ };
@@ -32,7 +32,13 @@
32
32
  getHoverStateProps,
33
33
  } from "../../utils/style-utils";
34
34
  import type { Snippet } from "svelte";
35
- import { mapBadge, mapDimension, mapLayerAlignment } from "./stack-utils";
35
+ import {
36
+ mapBadge,
37
+ mapDimension,
38
+ mapFixedHeightBounds,
39
+ mapFixedWidthBounds,
40
+ mapLayerAlignment,
41
+ } from "./stack-utils";
36
42
 
37
43
  interface MiscProps {
38
44
  onclick?: () => void;
@@ -89,6 +95,8 @@
89
95
  position: "relative",
90
96
  width: mapSize(size.width),
91
97
  height: mapSize(size.height),
98
+ ...mapFixedWidthBounds(size.width),
99
+ ...mapFixedHeightBounds(size.height),
92
100
  ...mapDimension(dimension),
93
101
  gap: px(spacing),
94
102
  margin: mapSpacing(margin),
@@ -1,6 +1,9 @@
1
+ import type { Size } from "../../types";
1
2
  import type { Dimension, ZAlignment } from "../../types/alignment";
2
3
  import type { Badge } from "../../types/components/stack";
3
4
  import { type CSS } from "../../utils/base-utils";
5
+ export declare function mapFixedWidthBounds(width: Size): CSS;
6
+ export declare function mapFixedHeightBounds(height: Size): CSS;
4
7
  export declare function mapDimension(dimension: Dimension): Record<string, string>;
5
8
  type JustifyAlign = {
6
9
  justifyContent: string;
@@ -14,6 +14,20 @@ const DISTRIBUTION_MAP = Object.freeze({
14
14
  center: "center",
15
15
  end: "flex-end",
16
16
  });
17
+ export function mapFixedWidthBounds(width) {
18
+ if (width.type !== "fixed") {
19
+ return {};
20
+ }
21
+ const value = px(width.value);
22
+ return { "min-width": value, "max-width": value };
23
+ }
24
+ export function mapFixedHeightBounds(height) {
25
+ if (height.type !== "fixed") {
26
+ return {};
27
+ }
28
+ const value = px(height.value);
29
+ return { "min-height": value, "max-height": value };
30
+ }
17
31
  export function mapDimension(dimension) {
18
32
  if (dimension.type === "zlayer") {
19
33
  return {};
@@ -21,7 +21,6 @@
21
21
  actionId: string,
22
22
  ) => void | Promise<void>;
23
23
  onBackClicked?: () => void;
24
- onNavigateToPage?: (pageId: string) => void;
25
24
  onClose?: () => void;
26
25
  containerId?: string;
27
26
  maxContentWidth?: string;
@@ -55,7 +54,6 @@
55
54
  onComponentInteraction,
56
55
  onPurchaseClicked,
57
56
  onBackClicked,
58
- onNavigateToPage,
59
57
  onClose,
60
58
  containerId = "screen-container",
61
59
  maxContentWidth,
@@ -94,7 +92,6 @@
94
92
  {onCompleteWorkflowNavigate}
95
93
  onVisitCustomerCenterClicked={() => onVisitCustomerCenterClicked?.()}
96
94
  {onBackClicked}
97
- {onNavigateToPage}
98
95
  {onClose}
99
96
  onRestorePurchasesClicked={() => onRestorePurchasesClicked?.()}
100
97
  onActionTriggered={(actionId: string) => {
@@ -16,7 +16,6 @@ interface Props {
16
16
  onComponentInteraction?: OnComponentInteraction;
17
17
  onPurchaseClicked?: (packageId: string, actionId: string) => void | Promise<void>;
18
18
  onBackClicked?: () => void;
19
- onNavigateToPage?: (pageId: string) => void;
20
19
  onClose?: () => void;
21
20
  containerId?: string;
22
21
  maxContentWidth?: string;
@@ -9,6 +9,21 @@
9
9
  import type { UIConfig } from "../../types/ui-config";
10
10
  import type { ReservedAttribute } from "../../types/components/input-text";
11
11
  import type { WorkflowNavData } from "../../types/workflow-nav";
12
+ import type {
13
+ WorkflowStepTriggerAction,
14
+ WorkflowStep,
15
+ } from "../../types/workflow";
16
+
17
+ type TriggerActionValue = WorkflowStep["trigger_actions"][string];
18
+
19
+ function isStepTriggerAction(
20
+ a: TriggerActionValue,
21
+ ): a is WorkflowStepTriggerAction {
22
+ return (
23
+ a.type === "step" &&
24
+ typeof (a as WorkflowStepTriggerAction).step_id === "string"
25
+ );
26
+ }
12
27
 
13
28
  interface Props {
14
29
  workflow: WorkflowNavData;
@@ -71,34 +86,115 @@
71
86
  }: Props = $props();
72
87
 
73
88
  // ── Nav stack ─────────────────────────────────────────────────────────────
74
- // The stack always has at least the initial page. The current page is the
75
- // last entry. navigate_to_page pushes, navigate_back pops.
89
+ // Each entry pairs the screen ID (used to look up the page to render) with
90
+ // the step ID (used to resolve trigger_actions on button press). The current
91
+ // page is always the last entry; workflow actions push, navigate_back pops.
76
92
  //
77
93
  // We key the reset off workflow.initial_page_id (a stable string) rather
78
94
  // than workflow object identity, so hosts that recompute the prop object on
79
95
  // unrelated reactive updates don't accidentally snap the user back to the
80
96
  // first page mid-flow.
81
- let navStack = $state<string[]>([]);
97
+ interface NavEntry {
98
+ pageId: string;
99
+ stepId: string;
100
+ }
101
+ let navStack = $state<NavEntry[]>([]);
82
102
  let trackedInitialPageId = $state<string>();
83
103
 
84
- const currentPageId = $derived(
85
- navStack[navStack.length - 1] ?? workflow.initial_page_id,
104
+ const current = $derived(
105
+ navStack[navStack.length - 1] ?? {
106
+ pageId: workflow.initial_page_id,
107
+ stepId: workflow.initial_step_id,
108
+ },
86
109
  );
87
- const currentScreen = $derived(workflow.pages[currentPageId]);
110
+ const currentScreen = $derived(workflow.pages[current.pageId]);
88
111
 
89
112
  $effect.pre(() => {
90
113
  if (trackedInitialPageId !== workflow.initial_page_id) {
91
114
  trackedInitialPageId = workflow.initial_page_id;
92
- navStack = [workflow.initial_page_id];
115
+ navStack = [
116
+ {
117
+ pageId: workflow.initial_page_id,
118
+ stepId: workflow.initial_step_id,
119
+ },
120
+ ];
93
121
  }
94
122
  });
95
123
 
96
- function handleNavigateToPage(pageId: string) {
97
- if (!workflow.pages[pageId]) {
98
- console.warn(`[Workflow] navigate_to_page: unknown page_id "${pageId}"`);
124
+ /**
125
+ * Resolves a "workflow"-type button action and navigates to the next screen.
126
+ *
127
+ * Flow: component ID → step.triggers[].action_id → trigger_actions[action_id]
128
+ * → step_id → step.screen_id → push onto nav stack.
129
+ */
130
+ function handleActionTriggered(actionId: string) {
131
+ const currentStep = workflow.steps[current.stepId];
132
+ if (!currentStep) {
133
+ console.warn(
134
+ `[Workflow] onActionTriggered: no current step found for step_id "${current.stepId}"`,
135
+ );
136
+ return;
137
+ }
138
+
139
+ // Resolve the component ID to an action ID via the step's triggers array.
140
+ const resolvedActionId = currentStep.triggers.find(
141
+ (t) => t.component_id === actionId,
142
+ )?.action_id;
143
+
144
+ if (!resolvedActionId) {
145
+ console.warn(
146
+ `[Workflow] onActionTriggered: no trigger entry found for component "${actionId}" on step "${current.stepId}"`,
147
+ );
148
+ onActionTriggered?.(actionId);
149
+ return;
150
+ }
151
+
152
+ const triggerAction = currentStep.trigger_actions[resolvedActionId];
153
+ if (!triggerAction) {
154
+ console.warn(
155
+ `[Workflow] onActionTriggered: no trigger_action found for action_id "${resolvedActionId}" on step "${current.stepId}"`,
156
+ );
157
+ onActionTriggered?.(actionId);
158
+ return;
159
+ }
160
+
161
+ if (!isStepTriggerAction(triggerAction)) {
162
+ // Condition-based actions require a full workflow engine — fall back to
163
+ // the host's onActionTriggered for anything we can't resolve client-side.
164
+ console.warn(
165
+ `[Workflow] onActionTriggered: trigger_action is not a simple step action, falling back to onActionTriggered`,
166
+ );
167
+ onActionTriggered?.(actionId);
168
+ return;
169
+ }
170
+
171
+ const nextStep = workflow.steps[triggerAction.step_id];
172
+ if (!nextStep) {
173
+ console.warn(
174
+ `[Workflow] onActionTriggered: step_id "${triggerAction.step_id}" not found in workflow steps`,
175
+ );
176
+ return;
177
+ }
178
+
179
+ const nextPageId = nextStep.screen_id;
180
+ if (!nextPageId) {
181
+ console.warn(
182
+ `[Workflow] onActionTriggered: step "${triggerAction.step_id}" has no screen_id (logic/experiment step?)`,
183
+ );
184
+ return;
185
+ }
186
+
187
+ if (!workflow.pages[nextPageId]) {
188
+ console.warn(
189
+ `[Workflow] onActionTriggered: screen_id "${nextPageId}" not found in workflow pages`,
190
+ );
99
191
  return;
100
192
  }
101
- navStack = [...navStack, pageId];
193
+
194
+ navStack = [
195
+ ...navStack,
196
+ { pageId: nextPageId, stepId: triggerAction.step_id },
197
+ ];
102
198
  }
103
199
 
104
200
  function handleBackClicked() {
@@ -111,7 +207,7 @@
111
207
  }
112
208
  </script>
113
209
 
114
- {#key currentPageId}
210
+ {#key current.pageId}
115
211
  <Screen
116
212
  paywallComponents={currentScreen}
117
213
  {uiConfig}
@@ -124,7 +220,7 @@
124
220
  {walletButtonRender}
125
221
  {safeAreaFallbackColor}
126
222
  {onPurchaseClicked}
127
- {onActionTriggered}
223
+ onActionTriggered={handleActionTriggered}
128
224
  {onInputChanged}
129
225
  {onReservedAttributeChanged}
130
226
  {onCompleteWorkflowNavigate}
@@ -132,7 +228,6 @@
132
228
  {onRestorePurchasesClicked}
133
229
  {onVisitCustomerCenterClicked}
134
230
  {onComponentInteraction}
135
- onNavigateToPage={handleNavigateToPage}
136
231
  onBackClicked={handleBackClicked}
137
232
  {onClose}
138
233
  />
@@ -1,12 +1,10 @@
1
1
  import type { WorkflowNavData } from "../../../types/workflow-nav";
2
2
  /**
3
- * Two-page workflow fixture that exercises navigate_to_page, navigate_back,
4
- * and close_workflow button actions via the Workflow component.
3
+ * Two-page workflow fixture. The "Next" button uses a "workflow" action, whose
4
+ * destination is resolved client-side via the step's trigger_actions map.
5
5
  *
6
6
  * Page layout:
7
- * "rcpaywall_welcome" — welcome screen with a "Next →" navigate_to_page button
7
+ * "rcpaywall_welcome" — welcome screen with a "Next →" button
8
8
  * "rcpaywall_details" — details screen with "← Back" and "✕ Close" buttons
9
- *
10
- * Page IDs match a paywall rc_public_id as they would in a real SDK response.
11
9
  */
12
10
  export declare const TWO_PAGE_WORKFLOW: WorkflowNavData;
@@ -1,15 +1,48 @@
1
1
  /**
2
- * Two-page workflow fixture that exercises navigate_to_page, navigate_back,
3
- * and close_workflow button actions via the Workflow component.
2
+ * Two-page workflow fixture. The "Next" button uses a "workflow" action, whose
3
+ * destination is resolved client-side via the step's trigger_actions map.
4
4
  *
5
5
  * Page layout:
6
- * "rcpaywall_welcome" — welcome screen with a "Next →" navigate_to_page button
6
+ * "rcpaywall_welcome" — welcome screen with a "Next →" button
7
7
  * "rcpaywall_details" — details screen with "← Back" and "✕ Close" buttons
8
- *
9
- * Page IDs match a paywall rc_public_id as they would in a real SDK response.
10
8
  */
11
9
  export const TWO_PAGE_WORKFLOW = {
12
10
  initial_page_id: "rcpaywall_welcome",
11
+ initial_step_id: "step_welcome",
12
+ steps: {
13
+ step_welcome: {
14
+ id: "step_welcome",
15
+ screen_id: "rcpaywall_welcome",
16
+ type: "screen",
17
+ param_values: {},
18
+ trigger_actions: {
19
+ // "next_btn" is the action_id referenced by the triggers array entry
20
+ // for the "next_btn" button component. It resolves to step_details,
21
+ // whose screen_id is rcpaywall_details.
22
+ next_btn: { type: "step", step_id: "step_details" },
23
+ },
24
+ triggers: [
25
+ {
26
+ action_id: "next_btn",
27
+ component_id: "next_btn",
28
+ type: "on_press",
29
+ name: "Next button",
30
+ },
31
+ ],
32
+ outputs: {},
33
+ metadata: null,
34
+ },
35
+ step_details: {
36
+ id: "step_details",
37
+ screen_id: "rcpaywall_details",
38
+ type: "screen",
39
+ param_values: {},
40
+ trigger_actions: {},
41
+ triggers: [],
42
+ outputs: {},
43
+ metadata: null,
44
+ },
45
+ },
13
46
  pages: {
14
47
  rcpaywall_welcome: {
15
48
  id: "rcpaywall_welcome",
@@ -58,7 +91,6 @@ export const TWO_PAGE_WORKFLOW = {
58
91
  shadow: null,
59
92
  shape: null,
60
93
  components: [
61
- // ── Title ────────────────────────────────────────────────
62
94
  {
63
95
  type: "text",
64
96
  id: "welcome_title",
@@ -79,7 +111,6 @@ export const TWO_PAGE_WORKFLOW = {
79
111
  dark: { type: "hex", value: "transparent" },
80
112
  },
81
113
  },
82
- // ── Body ─────────────────────────────────────────────────
83
114
  {
84
115
  type: "text",
85
116
  id: "welcome_body",
@@ -100,15 +131,11 @@ export const TWO_PAGE_WORKFLOW = {
100
131
  dark: { type: "hex", value: "transparent" },
101
132
  },
102
133
  },
103
- // ── navigate_to_page button ───────────────────────────
104
134
  {
105
135
  type: "button",
106
136
  id: "next_btn",
107
137
  name: "Next",
108
- action: {
109
- type: "navigate_to_page",
110
- page_id: "rcpaywall_details",
111
- },
138
+ action: { type: "workflow" },
112
139
  stack: {
113
140
  type: "stack",
114
141
  id: "next_btn_stack",
@@ -121,7 +148,12 @@ export const TWO_PAGE_WORKFLOW = {
121
148
  },
122
149
  spacing: 0,
123
150
  margin: { top: 0, bottom: 0, leading: 24, trailing: 24 },
124
- padding: { top: 16, bottom: 16, leading: 24, trailing: 24 },
151
+ padding: {
152
+ top: 16,
153
+ bottom: 16,
154
+ leading: 24,
155
+ trailing: 24,
156
+ },
125
157
  background: {
126
158
  type: "color",
127
159
  value: {
@@ -187,8 +219,8 @@ export const TWO_PAGE_WORKFLOW = {
187
219
  background: {
188
220
  type: "color",
189
221
  value: {
190
- light: { type: "hex", value: "#fff4f0ff" },
191
- dark: { type: "hex", value: "#2e1a1aff" },
222
+ light: { type: "hex", value: "#f0fff4ff" },
223
+ dark: { type: "hex", value: "#1a2e1aff" },
192
224
  },
193
225
  },
194
226
  stack: {
@@ -211,7 +243,6 @@ export const TWO_PAGE_WORKFLOW = {
211
243
  shadow: null,
212
244
  shape: null,
213
245
  components: [
214
- // ── Title ────────────────────────────────────────────────
215
246
  {
216
247
  type: "text",
217
248
  id: "details_title",
@@ -224,15 +255,14 @@ export const TWO_PAGE_WORKFLOW = {
224
255
  margin: { top: 80, bottom: 16, leading: 24, trailing: 24 },
225
256
  padding: { top: 0, bottom: 0, leading: 0, trailing: 0 },
226
257
  color: {
227
- light: { type: "hex", value: "#2e1a1aff" },
228
- dark: { type: "hex", value: "#fff4f0ff" },
258
+ light: { type: "hex", value: "#1a2e1aff" },
259
+ dark: { type: "hex", value: "#f0fff4ff" },
229
260
  },
230
261
  background_color: {
231
262
  light: { type: "hex", value: "transparent" },
232
263
  dark: { type: "hex", value: "transparent" },
233
264
  },
234
265
  },
235
- // ── Body ─────────────────────────────────────────────────
236
266
  {
237
267
  type: "text",
238
268
  id: "details_body",
@@ -245,15 +275,14 @@ export const TWO_PAGE_WORKFLOW = {
245
275
  margin: { top: 0, bottom: 48, leading: 24, trailing: 24 },
246
276
  padding: { top: 0, bottom: 0, leading: 0, trailing: 0 },
247
277
  color: {
248
- light: { type: "hex", value: "#775555ff" },
249
- dark: { type: "hex", value: "#ccaaaaff" },
278
+ light: { type: "hex", value: "#337733ff" },
279
+ dark: { type: "hex", value: "#88cc88ff" },
250
280
  },
251
281
  background_color: {
252
282
  light: { type: "hex", value: "transparent" },
253
283
  dark: { type: "hex", value: "transparent" },
254
284
  },
255
285
  },
256
- // ── navigate_back button ──────────────────────────────
257
286
  {
258
287
  type: "button",
259
288
  id: "back_btn",
@@ -271,7 +300,12 @@ export const TWO_PAGE_WORKFLOW = {
271
300
  },
272
301
  spacing: 0,
273
302
  margin: { top: 0, bottom: 12, leading: 24, trailing: 24 },
274
- padding: { top: 16, bottom: 16, leading: 24, trailing: 24 },
303
+ padding: {
304
+ top: 16,
305
+ bottom: 16,
306
+ leading: 24,
307
+ trailing: 24,
308
+ },
275
309
  background: {
276
310
  type: "color",
277
311
  value: {
@@ -314,7 +348,6 @@ export const TWO_PAGE_WORKFLOW = {
314
348
  ],
315
349
  },
316
350
  },
317
- // ── close button ──────────────────────────────────────
318
351
  {
319
352
  type: "button",
320
353
  id: "close_btn",
@@ -332,7 +365,12 @@ export const TWO_PAGE_WORKFLOW = {
332
365
  },
333
366
  spacing: 0,
334
367
  margin: { top: 0, bottom: 40, leading: 24, trailing: 24 },
335
- padding: { top: 16, bottom: 16, leading: 24, trailing: 24 },
368
+ padding: {
369
+ top: 16,
370
+ bottom: 16,
371
+ leading: 24,
372
+ trailing: 24,
373
+ },
336
374
  background: {
337
375
  type: "color",
338
376
  value: {
@@ -11,10 +11,6 @@ interface RestorePurchasesAction {
11
11
  interface NavigateBackAction {
12
12
  type: "navigate_back";
13
13
  }
14
- interface NavigateToPageAction {
15
- type: "navigate_to_page";
16
- page_id: string;
17
- }
18
14
  interface CloseAction {
19
15
  type: "close_workflow";
20
16
  }
@@ -60,15 +56,12 @@ interface CompleteWorkflowAction {
60
56
  };
61
57
  url_query_params?: CompleteWorkflowUrlQueryParams;
62
58
  }
63
- export type Action = WorkflowAction | RestorePurchasesAction | NavigateBackAction | NavigateToPageAction | CloseAction | NavigateToAction | NavigateToSheetAction | NavigateToWebPurchase | NavigateToUrlAction | CompleteWorkflowAction;
59
+ export type Action = WorkflowAction | RestorePurchasesAction | NavigateBackAction | CloseAction | NavigateToAction | NavigateToSheetAction | NavigateToWebPurchase | NavigateToUrlAction | CompleteWorkflowAction;
64
60
  export interface ButtonProps extends BaseComponent {
65
61
  type: "button";
66
62
  action: Action;
67
63
  stack: StackProps;
68
64
  transition?: null;
69
- triggers?: {
70
- on_press?: string;
71
- };
72
65
  overrides?: Overrides<ButtonProps>;
73
66
  }
74
67
  export {};
@@ -16,7 +16,7 @@ interface PackageTransition {
16
16
  destinationPackageId: string;
17
17
  defaultPackageId?: string;
18
18
  }
19
- type ButtonInteractionValue = "workflow" | "navigate_to_url" | "navigate_back" | "navigate_to_page" | "close_workflow" | "restore_purchases" | "navigate_to_customer_center" | "screen_redirect" | "navigate_to_privacy_policy" | "navigate_to_terms" | "navigate_to_sheet" | "navigate_to_offer_code" | "navigate_to_web_paywall_link";
19
+ type ButtonInteractionValue = "workflow" | "navigate_to_url" | "navigate_back" | "close_workflow" | "restore_purchases" | "navigate_to_customer_center" | "screen_redirect" | "navigate_to_privacy_policy" | "navigate_to_terms" | "navigate_to_sheet" | "navigate_to_offer_code" | "navigate_to_web_paywall_link";
20
20
  export type TabInteractionData = ComponentInteractionBase<"tab"> & IndexedTransition;
21
21
  export type SwitchInteractionData = ComponentInteractionBase<"switch", "on" | "off">;
22
22
  export type CarouselInteractionData = ComponentInteractionBase<"carousel"> & IndexedTransition;
@@ -1,27 +1,33 @@
1
- import type { WorkflowData, WorkflowScreen } from "./workflow";
1
+ import type { WorkflowData, WorkflowScreen, WorkflowStep } from "./workflow";
2
2
  /**
3
3
  * A self-contained multi-page workflow payload for use with the Workflow
4
4
  * component. This mirrors the shape of rc-workflows' WorkflowData but only
5
- * includes the fields needed for client-side page navigation — it does NOT
6
- * include step trigger_actions or experiment logic, which remain rc-workflows
7
- * concerns.
5
+ * includes the fields needed for client-side page navigation.
8
6
  */
9
7
  export interface WorkflowNavData {
10
8
  /** ID of the first page to display. Must be a key in `pages`. */
11
9
  initial_page_id: string;
12
10
  /**
13
- * All pages in this workflow keyed by page ID (the screen's rc_public_id).
14
- * `navigate_to_page` actions reference these IDs directly.
11
+ * All pages in this workflow keyed by screen ID.
12
+ * Workflow step actions resolve to these IDs via `steps[stepId].screen_id`.
15
13
  */
16
14
  pages: Record<string, WorkflowScreen>;
15
+ /**
16
+ * All steps in this workflow keyed by step ID.
17
+ * Used to resolve `workflow`-type button actions: the button's component ID
18
+ * is looked up in the current step's `triggers` array to get an action_id,
19
+ * which maps to a `trigger_actions` entry pointing to the next step_id,
20
+ * whose screen_id is then used to navigate to the correct page.
21
+ */
22
+ steps: Record<string, WorkflowStep>;
23
+ /** The step ID of the first step, corresponding to `initial_page_id`. */
24
+ initial_step_id: string;
17
25
  }
18
26
  /**
19
27
  * Map a full `WorkflowData` SDK response to the `WorkflowNavData` shape
20
28
  * consumed by the `Workflow` component.
21
29
  *
22
- * - `pages` is taken directly from `WorkflowData.screens` (already keyed by
23
- * screen/paywall rc_public_id, which is what `navigate_to_page` page_ids
24
- * reference).
30
+ * - `pages` is taken directly from `WorkflowData.screens`, keyed by screen ID.
25
31
  * - `initial_page_id` is resolved by looking up the screen_id of the initial
26
32
  * step, since `initial_step_id` is a step ID, not a screen ID.
27
33
  *
@@ -2,9 +2,7 @@
2
2
  * Map a full `WorkflowData` SDK response to the `WorkflowNavData` shape
3
3
  * consumed by the `Workflow` component.
4
4
  *
5
- * - `pages` is taken directly from `WorkflowData.screens` (already keyed by
6
- * screen/paywall rc_public_id, which is what `navigate_to_page` page_ids
7
- * reference).
5
+ * - `pages` is taken directly from `WorkflowData.screens`, keyed by screen ID.
8
6
  * - `initial_page_id` is resolved by looking up the screen_id of the initial
9
7
  * step, since `initial_step_id` is a step ID, not a screen ID.
10
8
  *
@@ -27,8 +25,10 @@ export function workflowDataToNavData(data) {
27
25
  }
28
26
  return {
29
27
  initial_page_id: initialPageId,
30
- // Shared by reference intentionally — screens are never mutated after
28
+ initial_step_id: data.initial_step_id,
29
+ // Shared by reference intentionally — screens/steps are never mutated after
31
30
  // WorkflowData is received from the SDK, so a copy would be wasteful.
32
31
  pages: data.screens,
32
+ steps: data.steps,
33
33
  };
34
34
  }
@@ -13,6 +13,27 @@ export interface WorkflowScreen extends PaywallData {
13
13
  revision: number;
14
14
  template_name: string;
15
15
  }
16
+ /**
17
+ * A trigger action that navigates directly to another step.
18
+ */
19
+ export interface WorkflowStepTriggerAction {
20
+ type: "step";
21
+ step_id: string;
22
+ }
23
+ /**
24
+ * An entry in a step's `triggers` array that maps a component interaction
25
+ * (e.g. button press) to a named action_id, which in turn maps to a
26
+ * `trigger_actions` entry.
27
+ */
28
+ export interface WorkflowStepTrigger {
29
+ /** The action ID to look up in `trigger_actions`. */
30
+ action_id: string;
31
+ /** The component (e.g. button) id that fires this trigger. */
32
+ component_id: string;
33
+ /** The interaction type, e.g. "on_press". */
34
+ type: string;
35
+ name?: string;
36
+ }
16
37
  /**
17
38
  * A single step in a workflow, referencing a screen by ID.
18
39
  * Aligns with the SDK response shape from khepri.
@@ -22,8 +43,19 @@ export interface WorkflowStep {
22
43
  screen_id?: string;
23
44
  type: string;
24
45
  param_values: Record<string, unknown>;
25
- trigger_actions: Record<string, unknown>;
26
- triggers: Record<string, unknown>;
46
+ /**
47
+ * Maps action IDs (from `triggers[].action_id`) to the next step to navigate
48
+ * to. Currently only `type: "step"` is supported for client-side resolution;
49
+ * condition-based actions are ignored.
50
+ */
51
+ trigger_actions: Record<string, WorkflowStepTriggerAction | Record<string, unknown>>;
52
+ /**
53
+ * Array of trigger entries mapping component interactions to action IDs.
54
+ * Resolve a button press by matching its component `id` against
55
+ * `component_id`, then use the entry's `action_id` to look up
56
+ * `trigger_actions`.
57
+ */
58
+ triggers: WorkflowStepTrigger[];
27
59
  outputs: Record<string, unknown>;
28
60
  metadata: Record<string, unknown> | null;
29
61
  }
@@ -75,7 +75,7 @@ function applyAlpha(baseColor, alpha) {
75
75
  function toHex(val) {
76
76
  return val.toString(16).padStart(2, "0").toUpperCase();
77
77
  }
78
- const textColorsForBackground = (backgroundColor, primaryColor, defaultColors, luminanceThreshold = DEFAULT_LUMINANCE_THRESHOLD) => {
78
+ const textColorsForBackground = (backgroundColor, primaryColor, primaryTextColorOverride, defaultColors, luminanceThreshold = DEFAULT_LUMINANCE_THRESHOLD) => {
79
79
  const textColors = {
80
80
  "grey-text-dark": defaultColors["grey-text-dark"],
81
81
  "grey-text-light": defaultColors["grey-text-light"],
@@ -91,7 +91,10 @@ const textColorsForBackground = (backgroundColor, primaryColor, defaultColors, l
91
91
  }
92
92
  }
93
93
  // Find the text color for the primary color
94
- if (primaryColor?.startsWith("#")) {
94
+ if (primaryTextColorOverride) {
95
+ textColors["primary-text"] = primaryTextColorOverride;
96
+ }
97
+ else if (primaryColor?.startsWith("#")) {
95
98
  const rgb = hexToRGB(primaryColor);
96
99
  if (rgb !== null) {
97
100
  textColors["primary-text"] = isLightColor({ ...rgb, luminanceThreshold })
@@ -125,7 +128,7 @@ const toColors = (colorsMapping, defaultColors, brandingAppearance) => {
125
128
  ? {
126
129
  ...defaultColors,
127
130
  ...mappedColors,
128
- ...textColorsForBackground(mappedColors.background, mappedColors.primary, defaultColors),
131
+ ...textColorsForBackground(mappedColors.background, mappedColors.primary, brandingAppearance.color_buttons_primary_text, defaultColors),
129
132
  ...colorsForButtonStates(mappedColors.primary),
130
133
  }
131
134
  : { ...defaultColors }; //copy, do not reference.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export interface BrandingAppearance {
7
7
  color_buttons_primary: string;
8
+ color_buttons_primary_text?: string | null;
8
9
  color_accent: string;
9
10
  color_error: string;
10
11
  color_product_info_bg: string;
@@ -13,11 +13,11 @@ function At() {
13
13
  });
14
14
  return { promise: n, resolve: e, reject: t };
15
15
  }
16
- const S = 2, we = 4, Ze = 8, Ft = 1 << 24, ne = 16, Y = 32, he = 64, kn = 128, L = 512, w = 1024, T = 2048, V = 4096, U = 8192, C = 16384, be = 32768, ct = 1 << 25, pe = 65536, dt = 1 << 17, Tn = 1 << 18, Pe = 1 << 19, An = 1 << 20, se = 65536, $e = 1 << 21, Je = 1 << 22, J = 1 << 23, ce = Symbol("$state"), Fn = Symbol("legacy props"), On = Symbol(""), X = new class extends Error {
16
+ const S = 2, we = 4, Ze = 8, Ft = 1 << 24, ne = 16, Y = 32, he = 64, kn = 128, L = 512, w = 1024, T = 2048, V = 4096, U = 8192, C = 16384, be = 32768, ct = 1 << 25, pe = 65536, dt = 1 << 17, Tn = 1 << 18, Pe = 1 << 19, An = 1 << 20, se = 65536, $e = 1 << 21, Je = 1 << 22, J = 1 << 23, ce = Symbol("$state"), Fn = Symbol("legacy props"), In = Symbol(""), X = new class extends Error {
17
17
  name = "StaleReactionError";
18
18
  message = "The reaction that called `getAbortSignal()` was re-run or destroyed";
19
19
  }();
20
- function In() {
20
+ function On() {
21
21
  throw new Error("https://svelte.dev/e/async_derived_orphan");
22
22
  }
23
23
  function Ln() {
@@ -35,7 +35,7 @@ function Rn() {
35
35
  function Nn() {
36
36
  throw new Error("https://svelte.dev/e/state_unsafe_mutation");
37
37
  }
38
- function Ot(e) {
38
+ function It(e) {
39
39
  return e === this.v;
40
40
  }
41
41
  function Pn(e, t) {
@@ -128,16 +128,16 @@ function y(e, t) {
128
128
  function Qe(e) {
129
129
  (e.f & L) !== 0 || e.deps === null ? y(e, w) : y(e, V);
130
130
  }
131
- function It(e) {
131
+ function Ot(e) {
132
132
  if (e !== null)
133
133
  for (const t of e)
134
- (t.f & S) === 0 || (t.f & se) === 0 || (t.f ^= se, It(
134
+ (t.f & S) === 0 || (t.f & se) === 0 || (t.f ^= se, Ot(
135
135
  /** @type {Derived} */
136
136
  t.deps
137
137
  ));
138
138
  }
139
139
  function Wn(e, t, n) {
140
- (e.f & T) !== 0 ? t.add(e) : (e.f & V) !== 0 && n.add(e), It(e.deps), y(e, w);
140
+ (e.f & T) !== 0 ? t.add(e) : (e.f & V) !== 0 && n.add(e), Ot(e.deps), y(e, w);
141
141
  }
142
142
  let Ae = !1;
143
143
  function Xn(e) {
@@ -149,7 +149,7 @@ function Xn(e) {
149
149
  }
150
150
  }
151
151
  const G = /* @__PURE__ */ new Set();
152
- let b = null, N = null, Ue = null, ze = !1, fe = null, Oe = null;
152
+ let b = null, N = null, Ue = null, ze = !1, fe = null, Ie = null;
153
153
  var pt = 0;
154
154
  let Yn = 1;
155
155
  class _e {
@@ -270,7 +270,7 @@ class _e {
270
270
  }
271
271
  const t = this.#t;
272
272
  this.#t = [], this.apply();
273
- var n = fe = [], r = [], i = Oe = [];
273
+ var n = fe = [], r = [], i = Ie = [];
274
274
  for (const l of t)
275
275
  try {
276
276
  this.#h(l, n, r);
@@ -282,7 +282,7 @@ class _e {
282
282
  for (const l of i)
283
283
  s.schedule(l);
284
284
  }
285
- if (fe = null, Oe = null, this.#c() || this.#_()) {
285
+ if (fe = null, Ie = null, this.#c() || this.#_()) {
286
286
  this.#p(r), this.#p(n);
287
287
  for (const [l, o] of this.#s)
288
288
  Dt(l, o);
@@ -358,7 +358,7 @@ class _e {
358
358
  try {
359
359
  ze = !0, b = this, this.#d();
360
360
  } finally {
361
- pt = 0, Ue = null, fe = null, Oe = null, ze = !1, b = null, N = null, Q.clear();
361
+ pt = 0, Ue = null, fe = null, Ie = null, ze = !1, b = null, N = null, Q.clear();
362
362
  }
363
363
  }
364
364
  discard() {
@@ -643,7 +643,7 @@ function Be(e) {
643
643
  ctx: B,
644
644
  deps: null,
645
645
  effects: null,
646
- equals: Ot,
646
+ equals: It,
647
647
  f: t,
648
648
  fn: e,
649
649
  reactions: null,
@@ -663,7 +663,7 @@ function Zn(e, t, n) {
663
663
  /** @type {Effect | null} */
664
664
  v
665
665
  );
666
- r === null && In();
666
+ r === null && On();
667
667
  var i = (
668
668
  /** @type {Promise<V>} */
669
669
  /** @type {unknown} */
@@ -803,7 +803,7 @@ function jt(e, t) {
803
803
  // TODO ideally we could skip this altogether, but it causes type errors
804
804
  v: e,
805
805
  reactions: null,
806
- equals: Ot,
806
+ equals: It,
807
807
  rv: 0,
808
808
  wv: 0
809
809
  };
@@ -819,7 +819,7 @@ function Z(e, t, n = !1) {
819
819
  // to ensure we error if state is set inside an inspect effect
820
820
  (!P || (p.f & dt) !== 0) && xe() && (p.f & (S | ne | Je | dt)) !== 0 && (M === null || !de.call(M, e)) && Nn();
821
821
  let r = n ? ue(t) : t;
822
- return We(e, r, Oe);
822
+ return We(e, r, Ie);
823
823
  }
824
824
  function We(e, t, n = null) {
825
825
  if (!e.equals(t)) {
@@ -833,7 +833,7 @@ function We(e, t, n = null) {
833
833
  );
834
834
  (e.f & T) !== 0 && tt(s), N === null && Qe(s);
835
835
  }
836
- e.wv = Jt(), Ht(e, T, n), xe() && v !== null && (v.f & w) !== 0 && (v.f & (Y | he)) === 0 && (O === null ? gr([e]) : O.push(e)), !i.is_fork && Ve.size > 0 && !zt && tr();
836
+ e.wv = Jt(), Ht(e, T, n), xe() && v !== null && (v.f & w) !== 0 && (v.f & (Y | he)) === 0 && (I === null ? gr([e]) : I.push(e)), !i.is_fork && Ve.size > 0 && !zt && tr();
837
837
  }
838
838
  return t;
839
839
  }
@@ -1217,7 +1217,7 @@ function vr(e, t) {
1217
1217
  t.append(n), n = i;
1218
1218
  }
1219
1219
  }
1220
- let Ie = !1, le = !1;
1220
+ let Oe = !1, le = !1;
1221
1221
  function gt(e) {
1222
1222
  le = e;
1223
1223
  }
@@ -1233,9 +1233,9 @@ let M = null;
1233
1233
  function Kt(e) {
1234
1234
  p !== null && (M === null ? M = [e] : M.push(e));
1235
1235
  }
1236
- let A = null, F = 0, O = null;
1236
+ let A = null, F = 0, I = null;
1237
1237
  function gr(e) {
1238
- O = e;
1238
+ I = e;
1239
1239
  }
1240
1240
  let Zt = 1, re = 0, ie = re;
1241
1241
  function bt(e) {
@@ -1286,9 +1286,9 @@ function Qt(e, t, n = !0) {
1286
1286
  }
1287
1287
  }
1288
1288
  function en(e) {
1289
- var t = A, n = F, r = O, i = p, s = M, a = B, l = P, o = ie, f = e.f;
1289
+ var t = A, n = F, r = I, i = p, s = M, a = B, l = P, o = ie, f = e.f;
1290
1290
  A = /** @type {null | Value[]} */
1291
- null, F = 0, O = null, p = (f & (Y | he)) === 0 ? e : null, M = null, Le(e.ctx), P = !1, ie = ++re, e.ac !== null && (Ut(() => {
1291
+ null, F = 0, I = null, p = (f & (Y | he)) === 0 ? e : null, M = null, Le(e.ctx), P = !1, ie = ++re, e.ac !== null && (Ut(() => {
1292
1292
  e.ac.abort(X);
1293
1293
  }), e.ac = null);
1294
1294
  try {
@@ -1310,11 +1310,11 @@ function en(e) {
1310
1310
  for (h = F; h < c.length; h++)
1311
1311
  (c[h].reactions ??= []).push(e);
1312
1312
  } else !_ && c !== null && F < c.length && (Se(e, F), c.length = F);
1313
- if (xe() && O !== null && !P && c !== null && (e.f & (S | V | T)) === 0)
1313
+ if (xe() && I !== null && !P && c !== null && (e.f & (S | V | T)) === 0)
1314
1314
  for (h = 0; h < /** @type {Source[]} */
1315
- O.length; h++)
1315
+ I.length; h++)
1316
1316
  Qt(
1317
- O[h],
1317
+ I[h],
1318
1318
  /** @type {Effect} */
1319
1319
  e
1320
1320
  );
@@ -1325,14 +1325,14 @@ function en(e) {
1325
1325
  if (t !== null)
1326
1326
  for (const g of t)
1327
1327
  g.rv = re;
1328
- O !== null && (r === null ? r = O : r.push(.../** @type {Source[]} */
1329
- O));
1328
+ I !== null && (r === null ? r = I : r.push(.../** @type {Source[]} */
1329
+ I));
1330
1330
  }
1331
1331
  return (e.f & J) !== 0 && (e.f ^= J), d;
1332
1332
  } catch (g) {
1333
1333
  return Un(g);
1334
1334
  } finally {
1335
- e.f ^= $e, A = t, F = n, O = r, p = i, M = s, Le(a), P = l, ie = o;
1335
+ e.f ^= $e, A = t, F = n, I = r, p = i, M = s, Le(a), P = l, ie = o;
1336
1336
  }
1337
1337
  }
1338
1338
  function br(e, t) {
@@ -1365,8 +1365,8 @@ function ge(e) {
1365
1365
  var t = e.f;
1366
1366
  if ((t & C) === 0) {
1367
1367
  y(e, w);
1368
- var n = v, r = Ie;
1369
- v = e, Ie = !0;
1368
+ var n = v, r = Oe;
1369
+ v = e, Oe = !0;
1370
1370
  try {
1371
1371
  (t & (ne | Ft)) !== 0 ? dr(e) : st(e), Xt(e);
1372
1372
  var i = en(e);
@@ -1374,7 +1374,7 @@ function ge(e) {
1374
1374
  var s;
1375
1375
  mn && Bn && (e.f & T) !== 0 && e.deps;
1376
1376
  } finally {
1377
- Ie = r, v = n;
1377
+ Oe = r, v = n;
1378
1378
  }
1379
1379
  }
1380
1380
  }
@@ -1404,7 +1404,7 @@ function k(e) {
1404
1404
  var l = a.v;
1405
1405
  return ((a.f & w) === 0 && a.reactions !== null || nn(a)) && (l = tt(a)), Q.set(a, l), l;
1406
1406
  }
1407
- var o = (a.f & L) === 0 && !P && p !== null && (Ie || (p.f & L) !== 0), f = (a.f & be) === 0;
1407
+ var o = (a.f & L) === 0 && !P && p !== null && (Oe || (p.f & L) !== 0), f = (a.f & be) === 0;
1408
1408
  ke(a) && (o && (a.f |= L), Ct(a)), o && !f && (Bt(a), tn(a));
1409
1409
  }
1410
1410
  if (N?.has(e))
@@ -1672,17 +1672,17 @@ function Fr(e, t, n = !1) {
1672
1672
  }), a || s(-1, null);
1673
1673
  }, i);
1674
1674
  }
1675
- function Or(e, t, n) {
1675
+ function Ir(e, t, n) {
1676
1676
  var r = e == null ? "" : "" + e;
1677
1677
  return t && (r = r ? r + " " + t : t), r === "" ? null : r;
1678
1678
  }
1679
- function Ir(e, t) {
1679
+ function Or(e, t) {
1680
1680
  return e == null ? null : String(e);
1681
1681
  }
1682
1682
  function an(e, t, n, r, i, s) {
1683
1683
  var a = e.__className;
1684
1684
  if (a !== n || a === void 0) {
1685
- var l = Or(n, r);
1685
+ var l = Ir(n, r);
1686
1686
  l == null ? e.removeAttribute("class") : e.className = l, e.__className = n;
1687
1687
  }
1688
1688
  return s;
@@ -1690,7 +1690,7 @@ function an(e, t, n, r, i, s) {
1690
1690
  function on(e, t, n, r) {
1691
1691
  var i = e.__style;
1692
1692
  if (i !== t) {
1693
- var s = Ir(t);
1693
+ var s = Or(t);
1694
1694
  s == null ? e.removeAttribute("style") : e.style.cssText = s, e.__style = t;
1695
1695
  }
1696
1696
  return r;
@@ -1698,7 +1698,7 @@ function on(e, t, n, r) {
1698
1698
  const Lr = Symbol("is custom element"), Mr = Symbol("is html");
1699
1699
  function yt(e, t, n, r) {
1700
1700
  var i = Dr(e);
1701
- i[t] !== (i[t] = n) && (t === "loading" && (e[On] = n), n == null ? e.removeAttribute(t) : typeof n != "string" && Rr(e).includes(t) ? e[t] = n : e.setAttribute(t, n));
1701
+ i[t] !== (i[t] = n) && (t === "loading" && (e[In] = n), n == null ? e.removeAttribute(t) : typeof n != "string" && Rr(e).includes(t) ? e[t] = n : e.setAttribute(t, n));
1702
1702
  }
1703
1703
  function Dr(e) {
1704
1704
  return (
@@ -1838,7 +1838,7 @@ const Cr = {
1838
1838
  regular: "400",
1839
1839
  medium: "500",
1840
1840
  semibold: "600"
1841
- }, I = {
1841
+ }, O = {
1842
1842
  12: "12px",
1843
1843
  14: "14px",
1844
1844
  16: "16px",
@@ -1856,13 +1856,13 @@ const Cr = {
1856
1856
  regular: "0"
1857
1857
  }, m = {
1858
1858
  heading2xl: {
1859
- fontSize: I[28],
1859
+ fontSize: O[28],
1860
1860
  lineHeight: j[120],
1861
1861
  fontWeight: z.semibold,
1862
1862
  letterSpacing: H.tight
1863
1863
  },
1864
1864
  headingXl: {
1865
- fontSize: I[24],
1865
+ fontSize: O[24],
1866
1866
  lineHeight: j[130],
1867
1867
  fontWeight: z.semibold,
1868
1868
  letterSpacing: H.regular
@@ -1880,37 +1880,37 @@ const Cr = {
1880
1880
  letterSpacing: H.regular
1881
1881
  },
1882
1882
  bodyBase: {
1883
- fontSize: I[16],
1883
+ fontSize: O[16],
1884
1884
  lineHeight: j[140],
1885
1885
  fontWeight: z.regular,
1886
1886
  letterSpacing: H.regular
1887
1887
  },
1888
1888
  bodySmall: {
1889
- fontSize: I[14],
1889
+ fontSize: O[14],
1890
1890
  lineHeight: j[140],
1891
1891
  fontWeight: z.regular,
1892
1892
  letterSpacing: H.regular
1893
1893
  },
1894
1894
  labelButton: {
1895
- fontSize: I[16],
1895
+ fontSize: O[16],
1896
1896
  lineHeight: j[140],
1897
1897
  fontWeight: z.regular,
1898
1898
  letterSpacing: H.tight
1899
1899
  },
1900
1900
  labelDefault: {
1901
- fontSize: I[14],
1901
+ fontSize: O[14],
1902
1902
  lineHeight: j[140],
1903
1903
  fontWeight: z.regular,
1904
1904
  letterSpacing: H.tight
1905
1905
  },
1906
1906
  captionDefault: {
1907
- fontSize: I[12],
1907
+ fontSize: O[12],
1908
1908
  lineHeight: j[140],
1909
1909
  fontWeight: z.regular,
1910
1910
  letterSpacing: H.regular
1911
1911
  },
1912
1912
  captionLink: {
1913
- fontSize: I[12],
1913
+ fontSize: O[12],
1914
1914
  lineHeight: j[140],
1915
1915
  fontWeight: z.regular,
1916
1916
  letterSpacing: H.regular
@@ -1918,19 +1918,19 @@ const Cr = {
1918
1918
  }, $ = {
1919
1919
  heading2xl: {
1920
1920
  ...m.heading2xl,
1921
- fontSize: I[36]
1921
+ fontSize: O[36]
1922
1922
  },
1923
1923
  headingXl: {
1924
1924
  ...m.headingXl,
1925
- fontSize: I[32]
1925
+ fontSize: O[32]
1926
1926
  },
1927
1927
  headingLg: {
1928
1928
  ...m.headingLg,
1929
- fontSize: I[24]
1929
+ fontSize: O[24]
1930
1930
  },
1931
1931
  headingMd: {
1932
1932
  ...m.headingMd,
1933
- fontSize: I[18]
1933
+ fontSize: O[18]
1934
1934
  },
1935
1935
  bodyBase: {
1936
1936
  ...m.bodyBase
@@ -2087,23 +2087,25 @@ function xt(e, t) {
2087
2087
  function He(e) {
2088
2088
  return e.toString(16).padStart(2, "0").toUpperCase();
2089
2089
  }
2090
- const Yr = (e, t, n, r = ot) => {
2091
- const i = {
2092
- "grey-text-dark": n["grey-text-dark"],
2093
- "grey-text-light": n["grey-text-light"],
2094
- "grey-ui-dark": n["grey-ui-dark"],
2095
- "grey-ui-light": n["grey-ui-light"],
2096
- "primary-text": n["primary-text"]
2090
+ const Yr = (e, t, n, r, i = ot) => {
2091
+ const s = {
2092
+ "grey-text-dark": r["grey-text-dark"],
2093
+ "grey-text-light": r["grey-text-light"],
2094
+ "grey-ui-dark": r["grey-ui-dark"],
2095
+ "grey-ui-light": r["grey-ui-light"],
2096
+ "primary-text": r["primary-text"]
2097
2097
  };
2098
2098
  if (e?.startsWith("#")) {
2099
- const s = Ee(e);
2100
- s !== null && Object.assign(i, Wr(s));
2099
+ const a = Ee(e);
2100
+ a !== null && Object.assign(s, Wr(a));
2101
2101
  }
2102
- if (t?.startsWith("#")) {
2103
- const s = Ee(t);
2104
- s !== null && (i["primary-text"] = at({ ...s, luminanceThreshold: r }) ? "black" : "white");
2102
+ if (n)
2103
+ s["primary-text"] = n;
2104
+ else if (t?.startsWith("#")) {
2105
+ const a = Ee(t);
2106
+ a !== null && (s["primary-text"] = at({ ...a, luminanceThreshold: i }) ? "black" : "white");
2105
2107
  }
2106
- return i;
2108
+ return s;
2107
2109
  }, qr = (e) => ({
2108
2110
  "primary-hover": xt(e, 0.1),
2109
2111
  "primary-pressed": xt(e, 0.15)
@@ -2128,6 +2130,7 @@ const Yr = (e, t, n, r = ot) => {
2128
2130
  ...Yr(
2129
2131
  r.background,
2130
2132
  r.primary,
2133
+ n.color_buttons_primary_text,
2131
2134
  t
2132
2135
  ),
2133
2136
  ...qr(r.primary)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@revenuecat/purchases-ui-js",
3
3
  "description": "Web components for Paywalls. Powered by RevenueCat",
4
4
  "private": false,
5
- "version": "4.7.5",
5
+ "version": "4.8.0",
6
6
  "author": {
7
7
  "name": "RevenueCat, Inc."
8
8
  },