@getrheo/flow-runtime 1.0.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 (126) hide show
  1. package/dist/agentPrompt/index.d.ts +72 -0
  2. package/dist/agentPrompt/index.js +739 -0
  3. package/dist/agentPrompt/index.js.map +1 -0
  4. package/dist/aiFlowGenerationMerge.d.ts +32 -0
  5. package/dist/aiFlowGenerationMerge.js +120 -0
  6. package/dist/aiFlowGenerationMerge.js.map +1 -0
  7. package/dist/animations.d.ts +110 -0
  8. package/dist/animations.js +312 -0
  9. package/dist/animations.js.map +1 -0
  10. package/dist/assignment.d.ts +7 -0
  11. package/dist/assignment.js +25 -0
  12. package/dist/assignment.js.map +1 -0
  13. package/dist/brandGradient.d.ts +57 -0
  14. package/dist/brandGradient.js +137 -0
  15. package/dist/brandGradient.js.map +1 -0
  16. package/dist/brandGradientManifestIssues.d.ts +11 -0
  17. package/dist/brandGradientManifestIssues.js +302 -0
  18. package/dist/brandGradientManifestIssues.js.map +1 -0
  19. package/dist/buildFlowPreview.d.ts +7 -0
  20. package/dist/buildFlowPreview.js +81 -0
  21. package/dist/buildFlowPreview.js.map +1 -0
  22. package/dist/buttonVariantChrome.d.ts +26 -0
  23. package/dist/buttonVariantChrome.js +59 -0
  24. package/dist/buttonVariantChrome.js.map +1 -0
  25. package/dist/checkboxGlyphStyle.d.ts +31 -0
  26. package/dist/checkboxGlyphStyle.js +241 -0
  27. package/dist/checkboxGlyphStyle.js.map +1 -0
  28. package/dist/choiceOptionSelection.d.ts +11 -0
  29. package/dist/choiceOptionSelection.js +120 -0
  30. package/dist/choiceOptionSelection.js.map +1 -0
  31. package/dist/colorAlpha.d.ts +8 -0
  32. package/dist/colorAlpha.js +48 -0
  33. package/dist/colorAlpha.js.map +1 -0
  34. package/dist/counterLayer.d.ts +42 -0
  35. package/dist/counterLayer.js +95 -0
  36. package/dist/counterLayer.js.map +1 -0
  37. package/dist/decisionEval.d.ts +27 -0
  38. package/dist/decisionEval.js +197 -0
  39. package/dist/decisionEval.js.map +1 -0
  40. package/dist/dropShadow.d.ts +26 -0
  41. package/dist/dropShadow.js +76 -0
  42. package/dist/dropShadow.js.map +1 -0
  43. package/dist/emailPasswordAuthValidation.d.ts +16 -0
  44. package/dist/emailPasswordAuthValidation.js +25 -0
  45. package/dist/emailPasswordAuthValidation.js.map +1 -0
  46. package/dist/flowBuilderRules.d.ts +15 -0
  47. package/dist/flowBuilderRules.js +368 -0
  48. package/dist/flowBuilderRules.js.map +1 -0
  49. package/dist/flowGraph.d.ts +19 -0
  50. package/dist/flowGraph.js +373 -0
  51. package/dist/flowGraph.js.map +1 -0
  52. package/dist/hyperlinkLabel.d.ts +19 -0
  53. package/dist/hyperlinkLabel.js +232 -0
  54. package/dist/hyperlinkLabel.js.map +1 -0
  55. package/dist/index.d.ts +48 -0
  56. package/dist/index.js +4200 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/interpolateTemplate.d.ts +44 -0
  59. package/dist/interpolateTemplate.js +188 -0
  60. package/dist/interpolateTemplate.js.map +1 -0
  61. package/dist/layerRotate.d.ts +10 -0
  62. package/dist/layerRotate.js +9 -0
  63. package/dist/layerRotate.js.map +1 -0
  64. package/dist/layerTypography.d.ts +36 -0
  65. package/dist/layerTypography.js +68 -0
  66. package/dist/layerTypography.js.map +1 -0
  67. package/dist/layers.d.ts +69 -0
  68. package/dist/layers.js +257 -0
  69. package/dist/layers.js.map +1 -0
  70. package/dist/layout/index.d.ts +57 -0
  71. package/dist/layout/index.js +151 -0
  72. package/dist/layout/index.js.map +1 -0
  73. package/dist/manifestBillingSlice.d.ts +17 -0
  74. package/dist/manifestBillingSlice.js +102 -0
  75. package/dist/manifestBillingSlice.js.map +1 -0
  76. package/dist/prepareAiGeneratedScreen.d.ts +17 -0
  77. package/dist/prepareAiGeneratedScreen.js +99 -0
  78. package/dist/prepareAiGeneratedScreen.js.map +1 -0
  79. package/dist/publish-exports.json +166 -0
  80. package/dist/responsive/breakpoints.d.ts +34 -0
  81. package/dist/responsive/breakpoints.js +52 -0
  82. package/dist/responsive/breakpoints.js.map +1 -0
  83. package/dist/responsive/index.d.ts +8 -0
  84. package/dist/responsive/index.js +307 -0
  85. package/dist/responsive/index.js.map +1 -0
  86. package/dist/responsive/layerResolve.d.ts +43 -0
  87. package/dist/responsive/layerResolve.js +168 -0
  88. package/dist/responsive/layerResolve.js.map +1 -0
  89. package/dist/responsive/merge.d.ts +19 -0
  90. package/dist/responsive/merge.js +74 -0
  91. package/dist/responsive/merge.js.map +1 -0
  92. package/dist/responsive/previewSafeAreaInsets.d.ts +14 -0
  93. package/dist/responsive/previewSafeAreaInsets.js +24 -0
  94. package/dist/responsive/previewSafeAreaInsets.js.map +1 -0
  95. package/dist/responsive/screenContainerResolve.d.ts +11 -0
  96. package/dist/responsive/screenContainerResolve.js +122 -0
  97. package/dist/responsive/screenContainerResolve.js.map +1 -0
  98. package/dist/responsive/screenShellInsets.d.ts +11 -0
  99. package/dist/responsive/screenShellInsets.js +26 -0
  100. package/dist/responsive/screenShellInsets.js.map +1 -0
  101. package/dist/restingMotion.d.ts +167 -0
  102. package/dist/restingMotion.js +484 -0
  103. package/dist/restingMotion.js.map +1 -0
  104. package/dist/rheoAgentManifestMerge.d.ts +33 -0
  105. package/dist/rheoAgentManifestMerge.js +55 -0
  106. package/dist/rheoAgentManifestMerge.js.map +1 -0
  107. package/dist/scaleInputStyle.d.ts +35 -0
  108. package/dist/scaleInputStyle.js +77 -0
  109. package/dist/scaleInputStyle.js.map +1 -0
  110. package/dist/scaleValidation.d.ts +9 -0
  111. package/dist/scaleValidation.js +21 -0
  112. package/dist/scaleValidation.js.map +1 -0
  113. package/dist/stateMachine.d.ts +105 -0
  114. package/dist/stateMachine.js +674 -0
  115. package/dist/stateMachine.js.map +1 -0
  116. package/dist/stepResponse-BXgoZ7o-.d.ts +112 -0
  117. package/dist/textInputValidation.d.ts +14 -0
  118. package/dist/textInputValidation.js +46 -0
  119. package/dist/textInputValidation.js.map +1 -0
  120. package/dist/translationPlaceholders.d.ts +9 -0
  121. package/dist/translationPlaceholders.js +52 -0
  122. package/dist/translationPlaceholders.js.map +1 -0
  123. package/dist/validation.d.ts +31 -0
  124. package/dist/validation.js +233 -0
  125. package/dist/validation.js.map +1 -0
  126. package/package.json +242 -0
package/dist/index.js ADDED
@@ -0,0 +1,4200 @@
1
+ import { permissionCaptureFieldKey, appReviewCaptureFieldKey, oauthLoginResponseKey, emailPasswordAuthResponseKey, OS_PERMISSION_OUTCOME_END, OS_PERMISSION_OUTCOME_CONTINUE, isInputLayer } from '@getrheo/contracts/layers';
2
+ import { resolveLocalizedText } from '@getrheo/contracts/localized';
3
+ import { EXTERNAL_SURFACE_NO_NEXT, collectDecisionFieldKeysFromNode } from '@getrheo/contracts/decisions';
4
+ import { migrateLegacyManifest, FlowManifestSchema, screenBackgroundPlaybackId, isScreenBackgroundPlaybackId } from '@getrheo/contracts';
5
+ import { FIELD_KEY_RE as FIELD_KEY_RE$1 } from '@getrheo/contracts/fields';
6
+ import { walkScreenLayers } from '@getrheo/contracts/screens';
7
+ import { deepMergeStyle } from '@getrheo/flow-runtime/responsive/merge';
8
+
9
+ // src/stateMachine/stepResponse.ts
10
+ var externalSurfaceResponseKey = (nodeId) => `surface_${nodeId}`;
11
+ var isEligibleConsumedDraft = (r) => {
12
+ switch (r.kind) {
13
+ case "text":
14
+ case "choice":
15
+ case "multiChoice":
16
+ case "scale":
17
+ case "cta":
18
+ case "carousel":
19
+ return true;
20
+ default:
21
+ return false;
22
+ }
23
+ };
24
+
25
+ // src/stateMachine/flowSession.ts
26
+ var findScreen = (manifest, screenId) => manifest.screens.find((s) => s.id === screenId);
27
+ var findExternalSurface = (manifest, nodeId) => (
28
+ // Older / hand-constructed manifests may omit `externalSurfaceNodes` entirely;
29
+ // treat that as "no surfaces" rather than crashing the state machine.
30
+ (manifest.externalSurfaceNodes ?? []).find((n) => n.id === nodeId)
31
+ );
32
+ var initFlowState = (manifest, sessionPartial) => ({
33
+ manifest,
34
+ currentScreenId: null,
35
+ pendingExternalSurface: null,
36
+ history: [],
37
+ responses: {},
38
+ session: {
39
+ locale: sessionPartial?.locale ?? manifest.defaultLocale,
40
+ platform: sessionPartial?.platform ?? "unknown",
41
+ sdkAttributes: sessionPartial?.sdkAttributes ?? {}
42
+ },
43
+ status: "idle",
44
+ startedAt: null,
45
+ completedAt: null
46
+ });
47
+
48
+ // src/brandGradient.ts
49
+ var BRAND_GRADIENT_PREFIX = "$brandGradient:";
50
+ var brandGradientToCss = (g) => {
51
+ const stops = g.stops.map((s) => `${s.color} ${(s.offset * 100).toFixed(0)}%`).join(", ");
52
+ if (g.type === "linear") {
53
+ const angle = g.angle ?? 180;
54
+ return `linear-gradient(${angle}deg, ${stops})`;
55
+ }
56
+ return `radial-gradient(circle, ${stops})`;
57
+ };
58
+ var isBrandGradientToken = (s) => s.startsWith(BRAND_GRADIENT_PREFIX);
59
+ var resolveBrandGradientToken = (branding, token) => {
60
+ if (!isBrandGradientToken(token)) return void 0;
61
+ const id = token.slice(BRAND_GRADIENT_PREFIX.length);
62
+ const preset = branding?.gradientPresets.find((x) => x.id === id);
63
+ return preset ? brandGradientToCss(preset) : void 0;
64
+ };
65
+ var rawThemedString = (palette, value) => {
66
+ if (value === void 0) return void 0;
67
+ if (typeof value === "string") return value;
68
+ return palette === "dark" ? value.dark ?? value.light : value.light ?? value.dark;
69
+ };
70
+ var brandGradientFromThemedColor = (branding, palette, value) => {
71
+ const raw = rawThemedString(palette, value);
72
+ if (raw === void 0 || !isBrandGradientToken(raw)) return void 0;
73
+ const id = raw.slice(BRAND_GRADIENT_PREFIX.length);
74
+ return branding?.gradientPresets.find((x) => x.id === id);
75
+ };
76
+ var brandGradientNativeLinear = (g) => {
77
+ if (g.type !== "linear") return null;
78
+ const angleDeg = g.angle ?? 180;
79
+ const \u03B8 = angleDeg * Math.PI / 180;
80
+ const ux = Math.sin(\u03B8);
81
+ const uy = -Math.cos(\u03B8);
82
+ return {
83
+ colors: g.stops.map((s) => s.color),
84
+ locations: g.stops.map((s) => s.offset),
85
+ start: { x: 0.5 - ux * 0.5, y: 0.5 - uy * 0.5 },
86
+ end: { x: 0.5 + ux * 0.5, y: 0.5 + uy * 0.5 }
87
+ };
88
+ };
89
+ var brandGradientSolidFallback = (g) => g.stops[0]?.color ?? "#808080";
90
+ var HEX_FOR_GRADIENT = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
91
+ var normalizeHexForGradient = (hex) => {
92
+ const t = hex.trim();
93
+ if (!HEX_FOR_GRADIENT.test(t)) return t;
94
+ if (t.length === 4) {
95
+ const [, r, g, b] = t;
96
+ return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
97
+ }
98
+ if (t.length === 9) return t.slice(0, 7).toLowerCase();
99
+ return t.toLowerCase();
100
+ };
101
+ var clampPct = (n) => Math.min(100, Math.max(0, n));
102
+ var buildLinearGradientCss = (angleDeg, stops) => {
103
+ const a = Number.isFinite(angleDeg) ? angleDeg : 180;
104
+ const sorted = [...stops].map((s) => ({
105
+ offsetPct: clampPct(Number.isFinite(s.offsetPct) ? s.offsetPct : 0),
106
+ color: normalizeHexForGradient(s.color)
107
+ })).sort((x, y) => x.offsetPct - y.offsetPct);
108
+ if (sorted.length < 2) {
109
+ throw new Error("linear gradient requires at least 2 color stops");
110
+ }
111
+ for (const s of sorted) {
112
+ if (!HEX_FOR_GRADIENT.test(s.color)) {
113
+ throw new Error(`invalid gradient stop color: ${s.color}`);
114
+ }
115
+ }
116
+ const body = sorted.map((s) => `${s.color} ${s.offsetPct.toFixed(0)}%`).join(", ");
117
+ return `linear-gradient(${a}deg, ${body})`;
118
+ };
119
+ var buildTwoStopLinearGradientCss = (angleDeg, color0, color1) => buildLinearGradientCss(angleDeg, [
120
+ { color: color0, offsetPct: 0 },
121
+ { color: color1, offsetPct: 100 }
122
+ ]);
123
+ var isStoredLinearGradientCss = (s) => /^\s*linear-gradient\s*\(/i.test(s.trim());
124
+ var parseLinearGradientCss = (s) => {
125
+ const t = s.trim();
126
+ const head = t.match(/^\s*linear-gradient\s*\(\s*([-0-9.]+)\s*deg\s*,\s*(.*)\)\s*$/is);
127
+ if (!head) return null;
128
+ const angleStr = head[1];
129
+ const innerRaw = head[2];
130
+ if (angleStr === void 0 || innerRaw === void 0) return null;
131
+ const angleDeg = Number(angleStr);
132
+ if (!Number.isFinite(angleDeg)) return null;
133
+ const inner = innerRaw.trim();
134
+ if (!inner) return null;
135
+ const parts = inner.split(/,(?=\s*#)/);
136
+ const stops = [];
137
+ for (const part of parts) {
138
+ const p = part.trim();
139
+ const m = p.match(/^(#[0-9a-fA-F]{3,8})\s+([-0-9.]+)\s*%$/i);
140
+ if (!m) return null;
141
+ const hex = m[1];
142
+ const offsetStr = m[2];
143
+ if (hex === void 0 || offsetStr === void 0) return null;
144
+ const color = normalizeHexForGradient(hex);
145
+ const offsetPct = Number(offsetStr);
146
+ if (!Number.isFinite(offsetPct)) return null;
147
+ if (!HEX_FOR_GRADIENT.test(color)) return null;
148
+ stops.push({ color, offsetPct });
149
+ }
150
+ if (stops.length < 2) return null;
151
+ return { angleDeg, stops };
152
+ };
153
+ var parseTwoStopLinearGradientCss = (s) => {
154
+ const p = parseLinearGradientCss(s);
155
+ if (!p || p.stops.length !== 2) return null;
156
+ const sorted = [...p.stops].sort((a, b) => a.offsetPct - b.offsetPct);
157
+ const s0 = sorted[0];
158
+ const s1 = sorted[1];
159
+ if (!s0 || !s1 || s0.offsetPct !== 0 || s1.offsetPct !== 100) return null;
160
+ return { angleDeg: p.angleDeg, color0: s0.color, color1: s1.color };
161
+ };
162
+ var nativeLinearFromAngleAndStops = (angleDeg, stops) => {
163
+ const sorted = [...stops].map((s) => ({
164
+ color: normalizeHexForGradient(s.color),
165
+ offsetPct: clampPct(Number.isFinite(s.offsetPct) ? s.offsetPct : 0)
166
+ })).sort((a, b) => a.offsetPct - b.offsetPct);
167
+ const \u03B8 = angleDeg * Math.PI / 180;
168
+ const ux = Math.sin(\u03B8);
169
+ const uy = -Math.cos(\u03B8);
170
+ return {
171
+ colors: sorted.map((s) => s.color),
172
+ locations: sorted.map((s) => s.offsetPct / 100),
173
+ start: { x: 0.5 - ux * 0.5, y: 0.5 - uy * 0.5 },
174
+ end: { x: 0.5 + ux * 0.5, y: 0.5 + uy * 0.5 }
175
+ };
176
+ };
177
+ var nativeLinearFromAngleAndTwoColors = (angleDeg, color0, color1) => nativeLinearFromAngleAndStops(angleDeg, [
178
+ { color: color0, offsetPct: 0 },
179
+ { color: color1, offsetPct: 100 }
180
+ ]);
181
+ var walkLayers = (root, fn) => {
182
+ const visit = (l, depth) => {
183
+ fn(l, depth);
184
+ if (l.kind === "stack") l.children.forEach((c) => visit(c, depth + 1));
185
+ else if (l.kind === "carousel") l.slides.forEach((c) => visit(c, depth + 1));
186
+ else if (l.kind === "button") l.children.forEach((c) => visit(c, depth + 1));
187
+ else if (l.kind === "back_button") l.children.forEach((c) => visit(c, depth + 1));
188
+ else if (l.kind === "hyperlink") l.children.forEach((c) => visit(c, depth + 1));
189
+ else if (l.kind === "single_choice" || l.kind === "multiple_choice") {
190
+ l.children.forEach((c) => visit(c, depth + 1));
191
+ } else if (l.kind === "text_input" || l.kind === "scale_input") {
192
+ l.children?.forEach((c) => visit(c, depth + 1));
193
+ } else if (l.kind === "oauth_login") {
194
+ l.children.forEach((c) => visit(c, depth + 1));
195
+ } else if (l.kind === "oauth_provider" && l.variant === "custom") {
196
+ l.children.forEach((c) => visit(c, depth + 1));
197
+ } else if (l.kind === "email_password_auth") {
198
+ l.children.forEach((c) => visit(c, depth + 1));
199
+ } else if (l.kind === "email_password_field") {
200
+ l.children?.forEach((c) => visit(c, depth + 1));
201
+ } else if (l.kind === "email_password_submit") {
202
+ l.children.forEach((c) => visit(c, depth + 1));
203
+ }
204
+ };
205
+ visit(root, 0);
206
+ };
207
+ var walkScreen = (screen, fn) => {
208
+ if (screen.regions.header) walkLayers(screen.regions.header, fn);
209
+ walkLayers(screen.regions.body, fn);
210
+ if (screen.regions.footer) walkLayers(screen.regions.footer, fn);
211
+ };
212
+ var findInputLayer = (screen) => {
213
+ let found = null;
214
+ walkScreen(screen, (l) => {
215
+ if (!found && isInputLayer(l)) found = l;
216
+ });
217
+ return found;
218
+ };
219
+ var findManualSubmitInputLayer = (screen) => {
220
+ const input = findInputLayer(screen);
221
+ if (!input) return null;
222
+ if (input.kind === "multiple_choice" || input.kind === "text_input" || input.kind === "scale_input") {
223
+ return input;
224
+ }
225
+ return null;
226
+ };
227
+ var screenHasContinueButton = (screen) => {
228
+ let found = false;
229
+ walkScreen(screen, (l) => {
230
+ if (l.kind === "button" && l.action.kind === "continue") found = true;
231
+ });
232
+ return found;
233
+ };
234
+ var findOptionStackForChoice = (layer, optionId) => {
235
+ const binding = layer.optionBindings.find((b) => b.optionId === optionId);
236
+ if (!binding) return null;
237
+ const stack = layer.children.find((c) => c.id === binding.rootLayerId);
238
+ return stack ?? null;
239
+ };
240
+ var choiceOptionLabel = (layer, optionId, locale) => {
241
+ const stack = findOptionStackForChoice(layer, optionId);
242
+ if (!stack) return "";
243
+ let foundText = null;
244
+ walkLayers(stack, (l) => {
245
+ if (foundText) return;
246
+ if (l.kind === "text") foundText = l.text;
247
+ });
248
+ if (foundText) return resolveLocalizedText(foundText, locale);
249
+ return optionId;
250
+ };
251
+ var findLayerById = (screen, id) => {
252
+ let found = null;
253
+ walkScreen(screen, (l) => {
254
+ if (!found && l.id === id) found = l;
255
+ });
256
+ return found;
257
+ };
258
+ var collectFieldKeys = (manifest) => {
259
+ const out = [];
260
+ for (const screen of manifest.screens) {
261
+ walkScreen(screen, (l) => {
262
+ if (isInputLayer(l)) out.push({ fieldKey: l.fieldKey, screenId: screen.id });
263
+ if (l.kind === "checkbox") out.push({ fieldKey: l.fieldKey, screenId: screen.id });
264
+ if (l.kind === "email_password_auth") {
265
+ out.push({ fieldKey: l.fieldKey, screenId: screen.id });
266
+ }
267
+ });
268
+ }
269
+ return out;
270
+ };
271
+ var nextUniqueFieldKey = (base, used) => {
272
+ const set = used instanceof Set ? used : new Set(used);
273
+ if (!set.has(base)) return base;
274
+ let n = 2;
275
+ while (set.has(`${base}_${n}`)) n += 1;
276
+ return `${base}_${n}`;
277
+ };
278
+ var resolveTokens = (theme, value) => {
279
+ if (value === void 0) return value;
280
+ if (typeof value !== "string" || !value.startsWith("$")) return value;
281
+ const key = value.slice(1);
282
+ const literal = theme?.[key];
283
+ if (typeof literal === "string") return literal;
284
+ return value;
285
+ };
286
+ var resolveThemedColor = (theme, palette, value) => {
287
+ if (value === void 0) return void 0;
288
+ if (typeof value === "string") return resolveTokens(theme, value);
289
+ const raw = palette === "dark" ? value.dark ?? value.light : value.light ?? value.dark;
290
+ if (raw === void 0) return void 0;
291
+ return resolveTokens(theme, raw);
292
+ };
293
+ var resolveThemedBackground = (theme, branding, palette, value) => {
294
+ if (value === void 0) return void 0;
295
+ if (typeof value === "string") {
296
+ if (value.startsWith("$brandGradient:")) {
297
+ return resolveBrandGradientToken(branding, value);
298
+ }
299
+ return resolveTokens(theme, value);
300
+ }
301
+ const raw = palette === "dark" ? value.dark ?? value.light : value.light ?? value.dark;
302
+ if (raw === void 0) return void 0;
303
+ if (raw.startsWith("$brandGradient:")) {
304
+ return resolveBrandGradientToken(branding, raw);
305
+ }
306
+ return resolveTokens(theme, raw);
307
+ };
308
+ var nativeBrandBackgroundFromThemedColor = (theme, branding, palette, value) => {
309
+ const preset = brandGradientFromThemedColor(branding, palette, value);
310
+ if (preset) {
311
+ const lin = brandGradientNativeLinear(preset);
312
+ if (lin) return { linear: lin };
313
+ return { solid: brandGradientSolidFallback(preset) };
314
+ }
315
+ const bg = resolveThemedBackground(theme, branding, palette, value);
316
+ if (!bg) return {};
317
+ const parsed = parseLinearGradientCss(bg);
318
+ if (parsed) {
319
+ return {
320
+ linear: nativeLinearFromAngleAndStops(
321
+ parsed.angleDeg,
322
+ parsed.stops.map((s) => ({ color: s.color, offsetPct: s.offsetPct }))
323
+ )
324
+ };
325
+ }
326
+ if (isStoredLinearGradientCss(bg)) {
327
+ const first = bg.match(/#[0-9a-fA-F]{3,8}/);
328
+ return { solid: first ? first[0] : "#808080" };
329
+ }
330
+ return { solid: bg };
331
+ };
332
+
333
+ // src/decisionEval.ts
334
+ var findDecisionNode = (manifest, id) => (manifest.decisionNodes ?? []).find((d) => d.id === id);
335
+ var decisionTargetIds = (manifest) => new Set((manifest.decisionNodes ?? []).map((d) => d.id));
336
+ var stableSerialize = (value) => {
337
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
338
+ if (Array.isArray(value)) return `[${value.map(stableSerialize).join(",")}]`;
339
+ const keys = Object.keys(value).sort();
340
+ const parts = keys.map((k) => `${JSON.stringify(k)}:${stableSerialize(value[k])}`);
341
+ return `{${parts.join(",")}}`;
342
+ };
343
+ var digestDecisionExpression = (expr) => {
344
+ const s = stableSerialize(expr);
345
+ let h = 5381;
346
+ for (let i = 0; i < s.length; i += 1) {
347
+ h = h * 33 ^ s.charCodeAt(i);
348
+ }
349
+ return `d${(h >>> 0).toString(16)}`;
350
+ };
351
+ var responseForField = (fieldKey, responses) => responses[fieldKey];
352
+ var normalizeResponseValue = (r) => {
353
+ if (!r || typeof r !== "object") return void 0;
354
+ const o = r;
355
+ switch (o.kind) {
356
+ case "choice":
357
+ return typeof o.choiceId === "string" ? o.choiceId : void 0;
358
+ case "multiChoice":
359
+ return Array.isArray(o.choiceIds) ? o.choiceIds : void 0;
360
+ case "text":
361
+ return typeof o.value === "string" ? o.value : void 0;
362
+ case "scale":
363
+ return typeof o.value === "number" ? o.value : void 0;
364
+ case "checkbox":
365
+ return typeof o.value === "boolean" ? o.value : void 0;
366
+ default:
367
+ return void 0;
368
+ }
369
+ };
370
+ var normalizeScalarForStringPred = (raw) => {
371
+ if (raw === void 0 || raw === null) return void 0;
372
+ if (typeof raw === "string") return raw;
373
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
374
+ return void 0;
375
+ };
376
+ var normalizeNumber = (raw) => {
377
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return void 0;
378
+ return raw;
379
+ };
380
+ var normalizeChoiceId = (raw) => typeof raw === "string" && raw.length > 0 ? raw : void 0;
381
+ var normalizeChoiceIds = (raw) => {
382
+ if (!Array.isArray(raw)) return void 0;
383
+ const ids = raw.filter((x) => typeof x === "string" && x.length > 0);
384
+ return ids;
385
+ };
386
+ var normalizeBoolean = (raw) => {
387
+ if (typeof raw === "boolean") return raw;
388
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw !== 0;
389
+ if (typeof raw === "string") {
390
+ const s = raw.trim().toLowerCase();
391
+ if (s === "true" || s === "1" || s === "yes") return true;
392
+ if (s === "false" || s === "0" || s === "no") return false;
393
+ }
394
+ return void 0;
395
+ };
396
+ var resolveVariableValue = (variable, ctx) => {
397
+ if (variable.kind === "builtin") {
398
+ if (variable.name === "locale") return ctx.locale;
399
+ return ctx.platform;
400
+ }
401
+ if (variable.kind === "sdk") return ctx.sdkAttributes[variable.key];
402
+ const r = responseForField(variable.fieldKey, ctx.responses);
403
+ return normalizeResponseValue(r);
404
+ };
405
+ var evalStringPred = (left, pred) => {
406
+ switch (pred.op) {
407
+ case "eq":
408
+ return left === pred.value;
409
+ case "neq":
410
+ return left !== pred.value;
411
+ case "contains":
412
+ return left.includes(pred.value);
413
+ default:
414
+ return false;
415
+ }
416
+ };
417
+ var evalNumberPred = (left, pred) => {
418
+ switch (pred.op) {
419
+ case "eq":
420
+ return left === pred.value;
421
+ case "neq":
422
+ return left !== pred.value;
423
+ case "lt":
424
+ return left < pred.value;
425
+ case "lte":
426
+ return left <= pred.value;
427
+ case "gt":
428
+ return left > pred.value;
429
+ case "gte":
430
+ return left >= pred.value;
431
+ default:
432
+ return false;
433
+ }
434
+ };
435
+ var evalChoicePred = (choiceId, pred) => {
436
+ if (pred.op === "eq") return choiceId === pred.optionId;
437
+ return pred.optionIds.includes(choiceId);
438
+ };
439
+ var setIntersect = (a, b) => {
440
+ const bs = new Set(b);
441
+ return a.filter((x) => bs.has(x));
442
+ };
443
+ var evalBooleanPred = (left, pred) => {
444
+ if (pred.op === "eq") return left === pred.value;
445
+ return left !== pred.value;
446
+ };
447
+ var evalMultiPred = (selected, pred) => {
448
+ const required = pred.optionIds;
449
+ switch (pred.op) {
450
+ case "intersects":
451
+ return setIntersect(selected, required).length > 0;
452
+ case "contains_all": {
453
+ const sel = new Set(selected);
454
+ return required.every((id) => sel.has(id));
455
+ }
456
+ case "subset_of": {
457
+ const allow = new Set(required);
458
+ return selected.every((id) => allow.has(id));
459
+ }
460
+ default:
461
+ return false;
462
+ }
463
+ };
464
+ var evaluateDecisionExpression = (expr, ctx) => {
465
+ if (expr.kind === "empty") return false;
466
+ if (expr.kind === "group") {
467
+ if (expr.op === "and") {
468
+ for (const c of expr.children) {
469
+ if (!evaluateDecisionExpression(c, ctx)) return false;
470
+ }
471
+ return true;
472
+ }
473
+ for (const c of expr.children) {
474
+ if (evaluateDecisionExpression(c, ctx)) return true;
475
+ }
476
+ return false;
477
+ }
478
+ const raw = resolveVariableValue(expr.variable, ctx);
479
+ const { predicate } = expr;
480
+ switch (predicate.type) {
481
+ case "string": {
482
+ const s = normalizeScalarForStringPred(raw);
483
+ if (s === void 0) return false;
484
+ return evalStringPred(s, predicate.pred);
485
+ }
486
+ case "number": {
487
+ const n = normalizeNumber(raw);
488
+ if (n === void 0) return false;
489
+ return evalNumberPred(n, predicate.pred);
490
+ }
491
+ case "choice": {
492
+ const id = normalizeChoiceId(raw);
493
+ if (!id) return false;
494
+ return evalChoicePred(id, predicate.pred);
495
+ }
496
+ case "multi": {
497
+ const ids = normalizeChoiceIds(raw);
498
+ if (!ids) return false;
499
+ return evalMultiPred(ids, predicate.pred);
500
+ }
501
+ case "boolean": {
502
+ const b = normalizeBoolean(raw);
503
+ if (b === void 0) return false;
504
+ return evalBooleanPred(b, predicate.pred);
505
+ }
506
+ default:
507
+ return false;
508
+ }
509
+ };
510
+ var evaluateDecisionNode = (node, ctx) => {
511
+ for (const c of node.cases) {
512
+ if (evaluateDecisionExpression(c.expression, ctx)) {
513
+ return {
514
+ matchedCaseId: c.id,
515
+ next: c.next,
516
+ clauseDigest: digestDecisionExpression(c.expression)
517
+ };
518
+ }
519
+ }
520
+ return {
521
+ matchedCaseId: null,
522
+ next: node.elseNext,
523
+ clauseDigest: "else"
524
+ };
525
+ };
526
+
527
+ // src/stateMachine/graphLanding.ts
528
+ var resolveOsPermissionDestination = (screen, response) => {
529
+ const layer = findLayerById(screen, response.layerId);
530
+ if (!layer || layer.kind !== "button" || layer.action.kind !== "request_os_permission")
531
+ return null;
532
+ if (layer.action.permissionKey !== response.permissionKey) return null;
533
+ const target = layer.action.outcomes[response.outcome];
534
+ if (target === OS_PERMISSION_OUTCOME_END) return null;
535
+ if (target === OS_PERMISSION_OUTCOME_CONTINUE) return screen.next.default ?? null;
536
+ return target;
537
+ };
538
+ var resolveNextScreenId = (screen, response) => {
539
+ if (response.kind === "screen_commit") {
540
+ return resolveNextScreenId(screen, response.primary);
541
+ }
542
+ if (response.kind === "go_to_screen") return response.screenId;
543
+ if (response.kind === "permission_outcome") {
544
+ const nextId = resolveOsPermissionDestination(screen, response);
545
+ return nextId ?? screen.next.default ?? null;
546
+ }
547
+ if (response.kind === "app_review_outcome") {
548
+ const layer = findLayerById(screen, response.layerId);
549
+ if (!layer || layer.kind !== "button" || layer.action.kind !== "request_app_review") {
550
+ return screen.next.default ?? null;
551
+ }
552
+ return screen.next.default ?? null;
553
+ }
554
+ if (response.kind === "oauth_login_resolve") {
555
+ return screen.next.default ?? null;
556
+ }
557
+ if (response.kind === "email_password_auth_resolve") {
558
+ return screen.next.default ?? null;
559
+ }
560
+ const input = findInputLayer(screen);
561
+ if (input && (input.kind === "single_choice" || input.kind === "multiple_choice") && input.branching.enabled) {
562
+ if (response.kind === "choice") {
563
+ const m = input.branching.conditions.find((c) => c.choiceId === response.choiceId);
564
+ if (m) return m.goTo;
565
+ } else if (response.kind === "multiChoice") {
566
+ const m = input.branching.conditions.find((c) => response.choiceIds.includes(c.choiceId));
567
+ if (m) return m.goTo;
568
+ }
569
+ }
570
+ return screen.next.default ?? null;
571
+ };
572
+ var resolveThroughGraph = (manifest, cursor, responses, session, onDecisionEvaluated) => {
573
+ let cur = cursor;
574
+ const evalCtx = {
575
+ locale: session.locale,
576
+ platform: session.platform,
577
+ sdkAttributes: session.sdkAttributes,
578
+ responses
579
+ };
580
+ while (cur) {
581
+ const dn = findDecisionNode(manifest, cur);
582
+ if (!dn) break;
583
+ const evalResult = evaluateDecisionNode(dn, evalCtx);
584
+ onDecisionEvaluated?.({
585
+ decisionNodeId: dn.id,
586
+ matchedCaseId: evalResult.matchedCaseId,
587
+ clauseDigest: evalResult.clauseDigest
588
+ });
589
+ cur = evalResult.next;
590
+ if (cur == null) break;
591
+ }
592
+ if (cur == null) return { kind: "end" };
593
+ if (findExternalSurface(manifest, cur)) return { kind: "surface", nodeId: cur };
594
+ return { kind: "screen", screenId: cur };
595
+ };
596
+ var responseKeyFor = (screen, response) => {
597
+ if (response.kind === "text" || response.kind === "choice" || response.kind === "multiChoice" || response.kind === "scale" || response.kind === "checkbox") {
598
+ const input = findInputLayer(screen);
599
+ if (input) return input.fieldKey;
600
+ }
601
+ if (response.kind === "permission_outcome") return permissionCaptureFieldKey(response.permissionKey);
602
+ if (response.kind === "app_review_outcome") return appReviewCaptureFieldKey(response.layerId);
603
+ if (response.kind === "checkbox") return response.fieldKey;
604
+ if (response.kind === "oauth_login_resolve") return oauthLoginResponseKey(response.layerId);
605
+ if (response.kind === "email_password_auth_resolve") {
606
+ return emailPasswordAuthResponseKey(response.layerId);
607
+ }
608
+ return screen.id;
609
+ };
610
+ var applyGraphLanding = (base, landing, nextResponses, now) => {
611
+ if (landing.kind === "end") {
612
+ return {
613
+ ...base,
614
+ responses: nextResponses,
615
+ currentScreenId: null,
616
+ pendingExternalSurface: null,
617
+ status: "completed",
618
+ completedAt: now
619
+ };
620
+ }
621
+ if (landing.kind === "surface") {
622
+ return {
623
+ ...base,
624
+ responses: nextResponses,
625
+ currentScreenId: null,
626
+ pendingExternalSurface: { nodeId: landing.nodeId },
627
+ history: [...base.history, landing.nodeId]
628
+ };
629
+ }
630
+ return {
631
+ ...base,
632
+ responses: nextResponses,
633
+ currentScreenId: landing.screenId,
634
+ pendingExternalSurface: null,
635
+ history: [...base.history, landing.screenId]
636
+ };
637
+ };
638
+ var startFlow = (state, second) => {
639
+ const opts = typeof second === "string" ? { now: second } : second ?? {};
640
+ const now = opts.now ?? (/* @__PURE__ */ new Date()).toISOString();
641
+ const entry = state.manifest.entryScreenId;
642
+ if (entry == null) {
643
+ return {
644
+ ...state,
645
+ status: "idle",
646
+ currentScreenId: null,
647
+ pendingExternalSurface: null,
648
+ history: [],
649
+ startedAt: null,
650
+ completedAt: null
651
+ };
652
+ }
653
+ const landing = resolveThroughGraph(
654
+ state.manifest,
655
+ entry,
656
+ {},
657
+ {
658
+ ...state.session
659
+ },
660
+ opts.onDecisionEvaluated
661
+ );
662
+ if (landing.kind === "end") {
663
+ return {
664
+ ...state,
665
+ status: "completed",
666
+ currentScreenId: null,
667
+ pendingExternalSurface: null,
668
+ history: [entry],
669
+ startedAt: now,
670
+ completedAt: now
671
+ };
672
+ }
673
+ if (landing.kind === "surface") {
674
+ return {
675
+ ...state,
676
+ status: "running",
677
+ currentScreenId: null,
678
+ pendingExternalSurface: { nodeId: landing.nodeId },
679
+ history: [landing.nodeId],
680
+ startedAt: now
681
+ };
682
+ }
683
+ return {
684
+ ...state,
685
+ status: "running",
686
+ currentScreenId: landing.screenId,
687
+ pendingExternalSurface: null,
688
+ history: [landing.screenId],
689
+ startedAt: now
690
+ };
691
+ };
692
+ var submitResponse = (state, response, third) => {
693
+ const opts = typeof third === "string" ? { now: third } : third ?? {};
694
+ const now = opts.now ?? (/* @__PURE__ */ new Date()).toISOString();
695
+ if (state.status !== "running") return state;
696
+ if (response.kind === "external_surface_outcome") {
697
+ const pending = state.pendingExternalSurface;
698
+ if (!pending || pending.nodeId !== response.nodeId) return state;
699
+ const surface = findExternalSurface(state.manifest, pending.nodeId);
700
+ if (!surface) return state;
701
+ const nextSession = response.sdkKeyPatch ? {
702
+ ...state.session,
703
+ sdkAttributes: { ...state.session.sdkAttributes, ...response.sdkKeyPatch }
704
+ } : state.session;
705
+ const target = surface.outcomes[response.outcome] ?? surface.fallback;
706
+ const nextResponses2 = {
707
+ ...state.responses,
708
+ [externalSurfaceResponseKey(pending.nodeId)]: response
709
+ };
710
+ if (target === EXTERNAL_SURFACE_NO_NEXT) {
711
+ return {
712
+ ...state,
713
+ session: nextSession,
714
+ responses: nextResponses2,
715
+ pendingExternalSurface: null,
716
+ currentScreenId: null,
717
+ status: "completed",
718
+ completedAt: now
719
+ };
720
+ }
721
+ const landing2 = resolveThroughGraph(
722
+ state.manifest,
723
+ target,
724
+ nextResponses2,
725
+ nextSession,
726
+ opts.onDecisionEvaluated
727
+ );
728
+ return applyGraphLanding(
729
+ { ...state, session: nextSession },
730
+ landing2,
731
+ nextResponses2,
732
+ now
733
+ );
734
+ }
735
+ if (!state.currentScreenId) return state;
736
+ const screen = findScreen(state.manifest, state.currentScreenId);
737
+ if (!screen) return state;
738
+ if (response.kind === "screen_commit") {
739
+ const mergedResponses = { ...state.responses };
740
+ for (const [fk, value] of Object.entries(response.checkboxValues)) {
741
+ mergedResponses[fk] = { kind: "checkbox", fieldKey: fk, value };
742
+ }
743
+ if (response.capturedDraft) {
744
+ mergedResponses[responseKeyFor(screen, response.capturedDraft)] = response.capturedDraft;
745
+ }
746
+ return submitResponse({ ...state, responses: mergedResponses }, response.primary, opts);
747
+ }
748
+ if (response.kind === "go_back") {
749
+ const fallback = response.fallbackScreenId;
750
+ if (state.history.length > 1) {
751
+ const nextHistory = state.history.slice(0, -1);
752
+ const prevId = nextHistory[nextHistory.length - 1];
753
+ if (!prevId) return state;
754
+ return {
755
+ ...state,
756
+ currentScreenId: prevId,
757
+ history: nextHistory
758
+ };
759
+ }
760
+ if (fallback && findScreen(state.manifest, fallback)) {
761
+ return {
762
+ ...state,
763
+ currentScreenId: fallback,
764
+ history: [fallback]
765
+ };
766
+ }
767
+ return state;
768
+ }
769
+ if (response.kind === "oauth_login_resolve" && !response.success) {
770
+ const key2 = oauthLoginResponseKey(response.layerId);
771
+ return {
772
+ ...state,
773
+ responses: { ...state.responses, [key2]: response }
774
+ };
775
+ }
776
+ if (response.kind === "email_password_auth_resolve" && !response.success) {
777
+ const key2 = emailPasswordAuthResponseKey(response.layerId);
778
+ return {
779
+ ...state,
780
+ responses: { ...state.responses, [key2]: response }
781
+ };
782
+ }
783
+ if (response.kind === "end_flow") {
784
+ const nextResponses2 = { ...state.responses };
785
+ if (response.consumedDraft) {
786
+ nextResponses2[responseKeyFor(screen, response.consumedDraft)] = response.consumedDraft;
787
+ }
788
+ nextResponses2[responseKeyFor(screen, { kind: "end_flow" })] = { kind: "end_flow" };
789
+ return {
790
+ ...state,
791
+ responses: nextResponses2,
792
+ currentScreenId: null,
793
+ status: "completed",
794
+ completedAt: now
795
+ };
796
+ }
797
+ if (response.kind === "skip") {
798
+ const nextResponses2 = { ...state.responses };
799
+ const manualInput = findManualSubmitInputLayer(screen);
800
+ if (manualInput) {
801
+ nextResponses2[manualInput.fieldKey] = { kind: "bypass_input", via: "skip" };
802
+ }
803
+ nextResponses2[responseKeyFor(screen, { kind: "skip" })] = { kind: "skip" };
804
+ const nextRaw2 = resolveNextScreenId(screen, { kind: "skip" });
805
+ const landing2 = resolveThroughGraph(
806
+ state.manifest,
807
+ nextRaw2,
808
+ nextResponses2,
809
+ state.session,
810
+ opts.onDecisionEvaluated
811
+ );
812
+ return applyGraphLanding(state, landing2, nextResponses2, now);
813
+ }
814
+ const key = responseKeyFor(screen, response);
815
+ const nextResponses = { ...state.responses, [key]: response };
816
+ if (response.kind === "go_to_screen") {
817
+ const manualInput = findManualSubmitInputLayer(screen);
818
+ if (manualInput) {
819
+ nextResponses[manualInput.fieldKey] = { kind: "bypass_input", via: "go_to_screen" };
820
+ }
821
+ }
822
+ const nextRaw = resolveNextScreenId(screen, response);
823
+ const landing = resolveThroughGraph(
824
+ state.manifest,
825
+ nextRaw,
826
+ nextResponses,
827
+ state.session,
828
+ opts.onDecisionEvaluated
829
+ );
830
+ return applyGraphLanding(state, landing, nextResponses, now);
831
+ };
832
+
833
+ // src/stateMachine/flowTerminal.ts
834
+ var TERMINAL_EXPORT_AUTH_RESPONSE_KEY_PREFIXES = ["oauth:", "email_pw:"];
835
+ var isAuthTerminalExportResponseKey = (key) => TERMINAL_EXPORT_AUTH_RESPONSE_KEY_PREFIXES.some((prefix) => key.startsWith(prefix));
836
+ var stripAuthResponsesForTerminalExport = (responses) => {
837
+ const out = {};
838
+ for (const [k, v] of Object.entries(responses)) {
839
+ if (isAuthTerminalExportResponseKey(k)) continue;
840
+ out[k] = v;
841
+ }
842
+ return out;
843
+ };
844
+ var stepResponseToCompletionValue = (r) => {
845
+ if (r.kind === "screen_commit") return void 0;
846
+ switch (r.kind) {
847
+ case "choice":
848
+ return r.choiceId;
849
+ case "multiChoice":
850
+ return r.choiceIds;
851
+ case "text":
852
+ return { value: r.value, classification: r.classification };
853
+ case "scale":
854
+ return r.value;
855
+ case "checkbox":
856
+ return r.value;
857
+ case "cta":
858
+ return r.action;
859
+ case "carousel":
860
+ return "carousel";
861
+ case "end_flow":
862
+ return "end_flow";
863
+ case "skip":
864
+ return "skip";
865
+ case "permission_outcome":
866
+ return r.outcome;
867
+ case "app_review_outcome":
868
+ return r.outcome;
869
+ case "oauth_login_resolve":
870
+ return {
871
+ success: r.success,
872
+ customerExternalId: r.customerExternalId,
873
+ provider: r.provider
874
+ };
875
+ case "email_password_auth_resolve":
876
+ return {
877
+ success: r.success,
878
+ mode: r.mode,
879
+ email: r.email
880
+ };
881
+ case "bypass_input":
882
+ return { bypassed: true, via: r.via };
883
+ case "external_surface_outcome":
884
+ return { outcome: r.outcome };
885
+ case "go_to_screen":
886
+ return { goToScreen: r.screenId };
887
+ case "go_back":
888
+ return void 0;
889
+ default: {
890
+ const _never = r;
891
+ return _never;
892
+ }
893
+ }
894
+ };
895
+ var buildCompletionResponses = (state) => {
896
+ const out = {};
897
+ for (const [key, r] of Object.entries(state.responses)) {
898
+ const v = stepResponseToCompletionValue(r);
899
+ if (v !== void 0) out[key] = v;
900
+ }
901
+ return out;
902
+ };
903
+ var abandonFlow = (state, now = (/* @__PURE__ */ new Date()).toISOString()) => ({
904
+ ...state,
905
+ status: "abandoned",
906
+ completedAt: now
907
+ });
908
+ var validateManifest = (data) => {
909
+ const migrated = migrateLegacyManifest(data);
910
+ const result = FlowManifestSchema.safeParse(migrated);
911
+ if (result.success) return { ok: true, manifest: result.data };
912
+ return {
913
+ ok: false,
914
+ issues: result.error.issues.map((i) => {
915
+ const screenIdx = i.path[1];
916
+ const screenId = typeof screenIdx === "number" && Array.isArray(data?.screens) ? data.screens[screenIdx]?.id : void 0;
917
+ return {
918
+ stepId: screenId ?? null,
919
+ path: [...i.path],
920
+ message: i.message,
921
+ code: i.code
922
+ };
923
+ })
924
+ };
925
+ };
926
+ var validatePublishable = (manifest) => {
927
+ const issues = [];
928
+ const warnings = [];
929
+ if (manifest.screens.length === 0) {
930
+ issues.push({
931
+ path: ["screens"],
932
+ message: "flow must have at least one screen",
933
+ code: "flow.no_screens"
934
+ });
935
+ }
936
+ if (manifest.entryScreenId == null && manifest.screens.length > 0) {
937
+ issues.push({
938
+ path: ["entryScreenId"],
939
+ message: "flow entry is not connected \u2014 connect the entry node on the canvas to a screen, decision, or integration step before publishing",
940
+ code: "flow.no_entry"
941
+ });
942
+ }
943
+ const screenMap = new Map(
944
+ manifest.screens.map((s) => [s.id, s])
945
+ );
946
+ const decisionMap = new Map((manifest.decisionNodes ?? []).map((d) => [d.id, d]));
947
+ const surfaceMap = new Map((manifest.externalSurfaceNodes ?? []).map((s) => [s.id, s]));
948
+ const enqueueGraphNode = (t) => {
949
+ if (t === null || t === void 0) return;
950
+ if (screenMap.has(t) || decisionMap.has(t) || surfaceMap.has(t)) {
951
+ queue.push(t);
952
+ }
953
+ };
954
+ const reachable = /* @__PURE__ */ new Set();
955
+ const queue = [];
956
+ if (manifest.entryScreenId != null) {
957
+ queue.push(manifest.entryScreenId);
958
+ }
959
+ let canTerminate = false;
960
+ while (queue.length) {
961
+ const id = queue.shift();
962
+ if (reachable.has(id)) continue;
963
+ reachable.add(id);
964
+ const decision = decisionMap.get(id);
965
+ if (decision) {
966
+ for (const c of decision.cases) {
967
+ if (c.next != null) enqueueGraphNode(c.next);
968
+ }
969
+ if (decision.elseNext != null) enqueueGraphNode(decision.elseNext);
970
+ continue;
971
+ }
972
+ const surface = surfaceMap.get(id);
973
+ if (surface) {
974
+ const outcomeTargets = Object.values(surface.outcomes);
975
+ if (outcomeTargets.some((t) => t === EXTERNAL_SURFACE_NO_NEXT) || surface.fallback === EXTERNAL_SURFACE_NO_NEXT) {
976
+ canTerminate = true;
977
+ }
978
+ if (surface.fallback != null && surface.fallback !== EXTERNAL_SURFACE_NO_NEXT) {
979
+ enqueueGraphNode(surface.fallback);
980
+ }
981
+ for (const t of outcomeTargets) {
982
+ if (t != null && t !== EXTERNAL_SURFACE_NO_NEXT) enqueueGraphNode(t);
983
+ }
984
+ continue;
985
+ }
986
+ const screen = screenMap.get(id);
987
+ if (!screen) continue;
988
+ const targets = [];
989
+ targets.push(screen.next.default);
990
+ const input = findInputLayer(screen);
991
+ if (input && (input.kind === "single_choice" || input.kind === "multiple_choice") && input.branching.enabled) {
992
+ for (const c of input.branching.conditions) targets.push(c.goTo);
993
+ }
994
+ walkScreen(screen, (l) => {
995
+ if (l.kind === "button" && l.action.kind === "go_to_step") {
996
+ targets.push(l.action.screenId);
997
+ }
998
+ if (l.kind === "button" && l.action.kind === "end_flow") {
999
+ targets.push(null);
1000
+ }
1001
+ if (l.kind === "button" && l.action.kind === "request_app_review") {
1002
+ targets.push(screen.next.default);
1003
+ }
1004
+ if (l.kind === "button" && l.action.kind === "request_os_permission") {
1005
+ const o = l.action.outcomes;
1006
+ for (const t of [o.granted, o.denied, o.blocked]) {
1007
+ if (t === OS_PERMISSION_OUTCOME_END) {
1008
+ targets.push(null);
1009
+ } else if (t === OS_PERMISSION_OUTCOME_CONTINUE) {
1010
+ targets.push(screen.next.default);
1011
+ } else {
1012
+ targets.push(t);
1013
+ }
1014
+ }
1015
+ }
1016
+ if (l.kind === "button" && l.action.kind === "go_back_one_screen" && l.action.fallbackScreenId) {
1017
+ targets.push(l.action.fallbackScreenId);
1018
+ }
1019
+ if (l.kind === "back_button" && l.fallbackScreenId) {
1020
+ targets.push(l.fallbackScreenId);
1021
+ }
1022
+ });
1023
+ if (targets.every((t) => t === null || t === void 0)) {
1024
+ canTerminate = true;
1025
+ }
1026
+ for (const t of targets) {
1027
+ if (t === null || t === void 0) {
1028
+ canTerminate = true;
1029
+ } else {
1030
+ enqueueGraphNode(t);
1031
+ }
1032
+ }
1033
+ }
1034
+ if (manifest.entryScreenId != null && !canTerminate) {
1035
+ issues.push({
1036
+ path: ["screens"],
1037
+ message: "no path from entry screen reaches completion",
1038
+ code: "flow.no_completion_path"
1039
+ });
1040
+ }
1041
+ for (const screen of manifest.screens) {
1042
+ if (manifest.entryScreenId != null && !reachable.has(screen.id)) {
1043
+ warnings.push({
1044
+ stepId: screen.id,
1045
+ path: ["screens", screen.id],
1046
+ message: `screen "${screen.id}" is not reachable from entry`,
1047
+ code: "screen.unreachable"
1048
+ });
1049
+ }
1050
+ }
1051
+ for (const sn of manifest.externalSurfaceNodes ?? []) {
1052
+ if (manifest.entryScreenId != null && !reachable.has(sn.id)) {
1053
+ warnings.push({
1054
+ stepId: null,
1055
+ path: ["externalSurfaceNodes", sn.id],
1056
+ message: `external surface "${sn.name ?? sn.id}" is not reachable from entry`,
1057
+ code: "external_surface.unreachable"
1058
+ });
1059
+ }
1060
+ }
1061
+ for (const dn of manifest.decisionNodes ?? []) {
1062
+ if (manifest.entryScreenId != null && !reachable.has(dn.id)) {
1063
+ warnings.push({
1064
+ stepId: null,
1065
+ path: ["decisionNodes", dn.id],
1066
+ message: `decision "${dn.id}" is not reachable from entry`,
1067
+ code: "decision.unreachable"
1068
+ });
1069
+ }
1070
+ for (const c of dn.cases) {
1071
+ if (c.next == null) {
1072
+ issues.push({
1073
+ stepId: null,
1074
+ path: ["decisionNodes", dn.id, "cases", c.id],
1075
+ message: `decision "${dn.id}" segment "${c.name ?? c.id}" must have a next step before publishing`,
1076
+ code: "decision.incomplete_branches"
1077
+ });
1078
+ }
1079
+ }
1080
+ if (dn.elseNext == null) {
1081
+ issues.push({
1082
+ stepId: null,
1083
+ path: ["decisionNodes", dn.id, "elseNext"],
1084
+ message: `decision "${dn.id}" needs an "everyone else" branch connected before publishing`,
1085
+ code: "decision.incomplete_branches"
1086
+ });
1087
+ }
1088
+ }
1089
+ return { ok: issues.length === 0, issues, warnings };
1090
+ };
1091
+ var BUILDER_RULES_AGENT_BULLETS = [
1092
+ "Connect flow entry on the canvas before publishing (entryScreenId must exist when screens are present).",
1093
+ "Every text and icon layer needs explicit style.color (including nested button label text \u2014 native does not inherit colors).",
1094
+ 'Screens with text_input, multiple_choice, or scale_input need a button with action.kind "continue".',
1095
+ "At most one input layer per screen (single_choice, multiple_choice, text_input, scale_input).",
1096
+ "Do not combine oauth_login or email_password_auth with other input layers on the same screen.",
1097
+ "Only one oauth_login and one email_password_auth per screen; never both on the same screen.",
1098
+ "fieldKey values must be unique snake_case across the flow.",
1099
+ "Choice branch goTo and go_to_step screenId must reference existing screen ids.",
1100
+ "request_app_review buttons require screen.next.default wired to a valid target.",
1101
+ 'request_os_permission outcomes must target existing screens, "continue", or "end".',
1102
+ 'Lottie/video with autoPlay false needs a button with action.kind "play_media" targeting that layer (or screen background video id).',
1103
+ "play_media targetLayerIds must reference Lottie/video layers on the same screen or the screen background video playback id."
1104
+ ];
1105
+ var FIELD_KEY_RE = /^[a-z][a-z0-9_]*$/;
1106
+ var styleBucketHasColor = (s) => s !== void 0 && s.color !== void 0;
1107
+ var textLayerHasAuthoringColor = (l) => {
1108
+ if (styleBucketHasColor(l.style)) return true;
1109
+ const bp = l.styleBreakpoints;
1110
+ if (!bp) return false;
1111
+ return styleBucketHasColor(bp.sm) || styleBucketHasColor(bp.md) || styleBucketHasColor(bp.lg) || styleBucketHasColor(bp.xl) || styleBucketHasColor(bp["2xl"]);
1112
+ };
1113
+ var iconLayerHasAuthoringColor = (l) => {
1114
+ if (styleBucketHasColor(l.style)) return true;
1115
+ const bp = l.styleBreakpoints;
1116
+ if (!bp) return false;
1117
+ return styleBucketHasColor(bp.sm) || styleBucketHasColor(bp.md) || styleBucketHasColor(bp.lg) || styleBucketHasColor(bp.xl) || styleBucketHasColor(bp["2xl"]);
1118
+ };
1119
+ var collectFlowBuilderIssues = (manifest) => {
1120
+ const issues = [];
1121
+ const fieldKeyOwners = /* @__PURE__ */ new Map();
1122
+ const screenIds = new Set(manifest.screens.map((s) => s.id));
1123
+ const jumpTargetIds = /* @__PURE__ */ new Set([
1124
+ ...screenIds,
1125
+ ...manifest.decisionNodes.map((d) => d.id),
1126
+ ...(manifest.externalSurfaceNodes ?? []).map((n) => n.id)
1127
+ ]);
1128
+ if (manifest.entryScreenId == null) {
1129
+ if (manifest.screens.length > 0) {
1130
+ issues.push(
1131
+ "Connect the flow entry node on the canvas to where the flow starts (a screen, decision, or integration step)."
1132
+ );
1133
+ }
1134
+ } else if (!jumpTargetIds.has(manifest.entryScreenId)) {
1135
+ issues.push(`Flow entry target "${manifest.entryScreenId}" does not exist.`);
1136
+ }
1137
+ for (const screen of manifest.screens) {
1138
+ let inputCount = 0;
1139
+ let oauthLoginLayerCount = 0;
1140
+ let emailPasswordAuthLayerCount = 0;
1141
+ let needsManualSubmit = false;
1142
+ let hasContinueButton = false;
1143
+ const screenLabel = screen.name || screen.id;
1144
+ const mediaLayerIds = /* @__PURE__ */ new Set();
1145
+ const buttonLayerIds = /* @__PURE__ */ new Set();
1146
+ const shellPlaybackId = screenBackgroundPlaybackId(screen.id);
1147
+ const shellFill = screen.containerStyle?.backgroundFill;
1148
+ const shellVideoFill = shellFill?.kind === "video" ? shellFill : void 0;
1149
+ walkScreen(screen, (l) => {
1150
+ if (l.kind === "button") buttonLayerIds.add(l.id);
1151
+ });
1152
+ if (shellFill?.kind === "image" || shellFill?.kind === "video") {
1153
+ if (!shellFill.media?.mediaAssetId) {
1154
+ issues.push(
1155
+ `Screen "${screenLabel}" ${shellFill.kind} background needs a media asset.`
1156
+ );
1157
+ }
1158
+ }
1159
+ if (shellVideoFill) {
1160
+ if (shellVideoFill.autoPlay === false) {
1161
+ const triggerId = shellVideoFill.triggerLayerId?.trim();
1162
+ if (!triggerId) {
1163
+ issues.push(
1164
+ `Screen "${screenLabel}" background video needs a trigger button when auto-play is off.`
1165
+ );
1166
+ } else if (!buttonLayerIds.has(triggerId)) {
1167
+ issues.push(
1168
+ `Screen "${screenLabel}" background video references a missing trigger button "${triggerId}".`
1169
+ );
1170
+ } else {
1171
+ const btn = findLayerById(screen, triggerId);
1172
+ if (!btn || btn.kind !== "button") {
1173
+ issues.push(
1174
+ `Screen "${screenLabel}" background video trigger must be a button layer.`
1175
+ );
1176
+ } else if (btn.action.kind !== "play_media") {
1177
+ issues.push(
1178
+ `Screen "${screenLabel}" background video trigger button must use On Tap \u2192 Play media.`
1179
+ );
1180
+ } else if (!btn.action.targetLayerIds.includes(shellPlaybackId)) {
1181
+ issues.push(
1182
+ `Screen "${screenLabel}" background video is not listed on trigger button "${triggerId}".`
1183
+ );
1184
+ }
1185
+ }
1186
+ } else if (shellVideoFill.triggerLayerId) {
1187
+ const btn = findLayerById(screen, shellVideoFill.triggerLayerId);
1188
+ if (btn?.kind === "button" && btn.action.kind === "play_media" && !btn.action.targetLayerIds.includes(shellPlaybackId)) {
1189
+ issues.push(
1190
+ `Screen "${screenLabel}" background video trigger button does not target the screen background.`
1191
+ );
1192
+ }
1193
+ }
1194
+ }
1195
+ walkScreen(screen, (l) => {
1196
+ if (l.kind === "lottie" || l.kind === "video") mediaLayerIds.add(l.id);
1197
+ if (l.kind === "oauth_login") {
1198
+ oauthLoginLayerCount += 1;
1199
+ }
1200
+ if (l.kind === "email_password_auth") {
1201
+ emailPasswordAuthLayerCount += 1;
1202
+ }
1203
+ if (l.kind === "button" && l.action.kind === "continue") {
1204
+ hasContinueButton = true;
1205
+ }
1206
+ if (isInputLayer(l)) {
1207
+ inputCount += 1;
1208
+ if (l.kind === "multiple_choice" || l.kind === "text_input" || l.kind === "scale_input") {
1209
+ needsManualSubmit = true;
1210
+ }
1211
+ const key = l.fieldKey;
1212
+ const label = screen.name || screen.id;
1213
+ if (!key || key.length === 0) {
1214
+ issues.push(`Screen "${label}" is missing a variable name (fieldKey).`);
1215
+ } else if (!FIELD_KEY_RE.test(key)) {
1216
+ issues.push(
1217
+ `Screen "${label}" has an invalid variable name "${key}" \u2014 use snake_case (a\u2013z, 0\u20139, _).`
1218
+ );
1219
+ } else {
1220
+ const owners = fieldKeyOwners.get(key) ?? [];
1221
+ owners.push(label);
1222
+ fieldKeyOwners.set(key, owners);
1223
+ }
1224
+ if (l.kind === "single_choice" || l.kind === "multiple_choice") {
1225
+ for (const cond of l.branching.conditions) {
1226
+ if (!screenIds.has(cond.goTo)) {
1227
+ issues.push(
1228
+ `Screen "${label}" branches choice "${cond.choiceId}" to a missing screen "${cond.goTo}".`
1229
+ );
1230
+ }
1231
+ }
1232
+ }
1233
+ }
1234
+ if (l.kind === "button" && l.action.kind === "go_to_step") {
1235
+ if (!screenIds.has(l.action.screenId)) {
1236
+ issues.push(
1237
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" targets a missing screen "${l.action.screenId}".`
1238
+ );
1239
+ }
1240
+ }
1241
+ if (l.kind === "button" && l.action.kind === "request_app_review") {
1242
+ const def = screen.next?.default;
1243
+ if (def == null) {
1244
+ issues.push(
1245
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" requests app review but the screen has no default next step.`
1246
+ );
1247
+ } else if (!screenIds.has(def) && !manifest.decisionNodes?.some((d) => d.id === def)) {
1248
+ issues.push(
1249
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" requests app review but default next "${def}" is missing.`
1250
+ );
1251
+ }
1252
+ }
1253
+ if (l.kind === "button" && l.action.kind === "request_os_permission") {
1254
+ const o = l.action.outcomes;
1255
+ for (const slot of ["granted", "denied", "blocked"]) {
1256
+ const sid = o[slot];
1257
+ if (sid === OS_PERMISSION_OUTCOME_END) {
1258
+ continue;
1259
+ }
1260
+ if (sid === OS_PERMISSION_OUTCOME_CONTINUE) {
1261
+ const def = screen.next?.default;
1262
+ if (def == null) {
1263
+ continue;
1264
+ }
1265
+ if (!screenIds.has(def) && !manifest.decisionNodes?.some((d) => d.id === def)) {
1266
+ issues.push(
1267
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" (${slot}) continues to missing target "${def}".`
1268
+ );
1269
+ }
1270
+ continue;
1271
+ }
1272
+ if (!screenIds.has(sid)) {
1273
+ issues.push(
1274
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" (${slot}) targets a missing screen "${sid}".`
1275
+ );
1276
+ }
1277
+ }
1278
+ }
1279
+ if (l.kind === "back_button" && l.fallbackScreenId && !screenIds.has(l.fallbackScreenId)) {
1280
+ issues.push(
1281
+ `Back button "${l.name || l.id}" on screen "${screen.name || screen.id}" uses a missing fallback screen "${l.fallbackScreenId}".`
1282
+ );
1283
+ }
1284
+ if (l.kind === "button" && l.action.kind === "go_back_one_screen" && l.action.fallbackScreenId) {
1285
+ if (!screenIds.has(l.action.fallbackScreenId)) {
1286
+ issues.push(
1287
+ `Button "${l.name || l.id}" on screen "${screen.name || screen.id}" uses a missing fallback screen "${l.action.fallbackScreenId}".`
1288
+ );
1289
+ }
1290
+ }
1291
+ if (l.kind === "lottie" || l.kind === "video") {
1292
+ const media = l;
1293
+ if (media.autoPlay === false) {
1294
+ const triggerId = media.triggerLayerId?.trim();
1295
+ if (!triggerId) {
1296
+ issues.push(
1297
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" needs a trigger button when auto-play is off.`
1298
+ );
1299
+ } else if (!buttonLayerIds.has(triggerId)) {
1300
+ issues.push(
1301
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" references a missing trigger button "${triggerId}".`
1302
+ );
1303
+ } else {
1304
+ const btn = findLayerById(screen, triggerId);
1305
+ if (!btn || btn.kind !== "button") {
1306
+ issues.push(
1307
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" trigger must be a button layer.`
1308
+ );
1309
+ } else if (btn.action.kind !== "play_media") {
1310
+ issues.push(
1311
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" trigger button must use On Tap \u2192 Play media (or pick the trigger again from this screen).`
1312
+ );
1313
+ } else if (!btn.action.targetLayerIds.includes(media.id)) {
1314
+ issues.push(
1315
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" is not listed on trigger button "${triggerId}".`
1316
+ );
1317
+ }
1318
+ }
1319
+ } else if (media.triggerLayerId) {
1320
+ const btn = findLayerById(screen, media.triggerLayerId);
1321
+ if (btn?.kind === "button" && btn.action.kind === "play_media" && !btn.action.targetLayerIds.includes(media.id)) {
1322
+ issues.push(
1323
+ `${media.kind === "video" ? "Video" : "Lottie"} "${media.name || media.id}" on screen "${screenLabel}" trigger button does not target this layer.`
1324
+ );
1325
+ }
1326
+ }
1327
+ }
1328
+ if (l.kind === "button" && l.action.kind === "play_media") {
1329
+ for (const targetId of l.action.targetLayerIds) {
1330
+ if (targetId === shellPlaybackId) {
1331
+ if (!shellVideoFill) {
1332
+ issues.push(
1333
+ `Button "${l.name || l.id}" on screen "${screenLabel}" targets screen background video, but this screen has no video background.`
1334
+ );
1335
+ }
1336
+ continue;
1337
+ }
1338
+ if (isScreenBackgroundPlaybackId(targetId)) {
1339
+ issues.push(
1340
+ `Button "${l.name || l.id}" on screen "${screenLabel}" play-media target "${targetId}" is not valid for this screen.`
1341
+ );
1342
+ continue;
1343
+ }
1344
+ if (!mediaLayerIds.has(targetId)) {
1345
+ issues.push(
1346
+ `Button "${l.name || l.id}" on screen "${screenLabel}" play-media target "${targetId}" must be a Lottie or video layer on this screen, or screen background video.`
1347
+ );
1348
+ }
1349
+ }
1350
+ }
1351
+ });
1352
+ if (oauthLoginLayerCount > 0 && inputCount > 0) {
1353
+ issues.push(
1354
+ `Screen "${screen.name || screen.id}" cannot combine OAuth Login with input layers (${inputCount}). Split them onto separate screens.`
1355
+ );
1356
+ }
1357
+ if (emailPasswordAuthLayerCount > 0 && inputCount > 0) {
1358
+ issues.push(
1359
+ `Screen "${screen.name || screen.id}" cannot combine Email / password login with input layers (${inputCount}). Split them onto separate screens.`
1360
+ );
1361
+ }
1362
+ if (oauthLoginLayerCount > 0 && emailPasswordAuthLayerCount > 0) {
1363
+ issues.push(
1364
+ `Screen "${screen.name || screen.id}" cannot combine OAuth Login with Email / password login. Use one login block per screen.`
1365
+ );
1366
+ }
1367
+ if (emailPasswordAuthLayerCount > 1) {
1368
+ issues.push(
1369
+ `Screen "${screen.name || screen.id}" has ${emailPasswordAuthLayerCount} Email / password login layers; only one is allowed per screen.`
1370
+ );
1371
+ }
1372
+ if (oauthLoginLayerCount > 1) {
1373
+ issues.push(
1374
+ `Screen "${screen.name || screen.id}" has ${oauthLoginLayerCount} OAuth Login layers; only one is allowed per screen.`
1375
+ );
1376
+ }
1377
+ if (inputCount > 1) {
1378
+ issues.push(
1379
+ `Screen "${screen.name || screen.id}" has ${inputCount} input layers; only one is allowed.`
1380
+ );
1381
+ }
1382
+ if (needsManualSubmit && !hasContinueButton) {
1383
+ issues.push(
1384
+ `Screen "${screen.name || screen.id}" has a multiple_choice, text_input, or scale_input but no Button with action "continue". Add a Continue button so users can submit.`
1385
+ );
1386
+ }
1387
+ }
1388
+ for (const [key, owners] of fieldKeyOwners) {
1389
+ if (owners.length > 1) {
1390
+ issues.push(`Variable name "${key}" is used by multiple screens: ${owners.join(", ")}.`);
1391
+ }
1392
+ }
1393
+ for (const screen of manifest.screens) {
1394
+ walkScreen(screen, (l) => {
1395
+ const screenLabel = screen.name || screen.id;
1396
+ if (l.kind === "text" && !textLayerHasAuthoringColor(l)) {
1397
+ issues.push(
1398
+ `Screen "${screenLabel}": text layer "${l.id}" must set style.color for light and dark (CSS inheritance does not apply on native).`
1399
+ );
1400
+ }
1401
+ if (l.kind === "icon" && !iconLayerHasAuthoringColor(l)) {
1402
+ issues.push(
1403
+ `Screen "${screenLabel}": icon layer "${l.id}" must set style.color for light and dark.`
1404
+ );
1405
+ }
1406
+ });
1407
+ }
1408
+ return issues;
1409
+ };
1410
+
1411
+ // src/assignment.ts
1412
+ var fnv1a = (input) => {
1413
+ let hash = 2166136261;
1414
+ for (let i = 0; i < input.length; i++) {
1415
+ hash ^= input.charCodeAt(i);
1416
+ hash = hash * 16777619 >>> 0;
1417
+ }
1418
+ return hash >>> 0;
1419
+ };
1420
+ var assignVariant = (experimentId, appUserId, variants) => {
1421
+ if (variants.length === 0) return null;
1422
+ const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
1423
+ if (totalWeight <= 0) return variants[0] ?? null;
1424
+ const bucket = fnv1a(`${experimentId}:${appUserId}`) % totalWeight;
1425
+ let acc = 0;
1426
+ for (const v of variants) {
1427
+ acc += v.weight;
1428
+ if (bucket < acc) return v;
1429
+ }
1430
+ return variants[variants.length - 1] ?? null;
1431
+ };
1432
+
1433
+ // src/brandGradientManifestIssues.ts
1434
+ var BP_KEYS = ["sm", "md", "lg", "xl", "2xl"];
1435
+ var themedStrings = (tc) => {
1436
+ if (tc === void 0) return [];
1437
+ if (typeof tc === "string") return [tc];
1438
+ const out = [];
1439
+ if (tc.light !== void 0) out.push(tc.light);
1440
+ if (tc.dark !== void 0) out.push(tc.dark);
1441
+ return out;
1442
+ };
1443
+ var pushIssue = (issues, stepId, layerId, field, message, code) => {
1444
+ issues.push({
1445
+ stepId,
1446
+ path: ["screens", stepId, layerId, field],
1447
+ message,
1448
+ code
1449
+ });
1450
+ };
1451
+ var checkThemed = (issues, stepId, layerId, field, tc, allowGradient, branding) => {
1452
+ for (const s of themedStrings(tc)) {
1453
+ if (isBrandGradientToken(s)) {
1454
+ if (!allowGradient) {
1455
+ pushIssue(
1456
+ issues,
1457
+ stepId,
1458
+ layerId,
1459
+ field,
1460
+ `Brand gradient presets can only be used on background fills (not ${field}).`,
1461
+ "brand_gradient.disallowed_field"
1462
+ );
1463
+ return;
1464
+ }
1465
+ if (branding !== void 0 && resolveBrandGradientToken(branding, s) === void 0) {
1466
+ pushIssue(
1467
+ issues,
1468
+ stepId,
1469
+ layerId,
1470
+ field,
1471
+ `Brand gradient preset not found: ${s}`,
1472
+ "brand_gradient.unknown_preset"
1473
+ );
1474
+ }
1475
+ continue;
1476
+ }
1477
+ if (isStoredLinearGradientCss(s)) {
1478
+ if (!allowGradient) {
1479
+ pushIssue(
1480
+ issues,
1481
+ stepId,
1482
+ layerId,
1483
+ field,
1484
+ `CSS linear gradients can only be used on background fills in v1 (not ${field}).`,
1485
+ "brand_gradient.linear_css_disallowed_field"
1486
+ );
1487
+ return;
1488
+ }
1489
+ }
1490
+ }
1491
+ };
1492
+ var checkCommon = (issues, stepId, layerId, fieldPrefix, s, branding) => {
1493
+ if (!s) return;
1494
+ checkThemed(issues, stepId, layerId, `${fieldPrefix}background`, s.background, true, branding);
1495
+ checkThemed(issues, stepId, layerId, `${fieldPrefix}border.color`, s.border?.color, false, branding);
1496
+ checkThemed(issues, stepId, layerId, `${fieldPrefix}shadow.color`, s.shadow?.color, false, branding);
1497
+ };
1498
+ var checkTextLike = (issues, stepId, layerId, fieldPrefix, s, branding) => {
1499
+ if (!s) return;
1500
+ checkCommon(issues, stepId, layerId, fieldPrefix, s, branding);
1501
+ checkThemed(issues, stepId, layerId, `${fieldPrefix}color`, s.color, false, branding);
1502
+ };
1503
+ var walkCommonBreakpoints = (issues, stepId, layerId, base, breakpoints, branding) => {
1504
+ checkCommon(issues, stepId, layerId, "style.", base, branding);
1505
+ if (!breakpoints) return;
1506
+ for (const k of BP_KEYS) {
1507
+ const patch = breakpoints[k];
1508
+ if (patch) checkCommon(issues, stepId, layerId, `styleBreakpoints.${k}.`, patch, branding);
1509
+ }
1510
+ };
1511
+ var walkTextBreakpoints = (issues, stepId, layerId, base, breakpoints, branding) => {
1512
+ checkTextLike(issues, stepId, layerId, "style.", base, branding);
1513
+ if (!breakpoints) return;
1514
+ for (const k of BP_KEYS) {
1515
+ const patch = breakpoints[k];
1516
+ if (patch) checkTextLike(issues, stepId, layerId, `styleBreakpoints.${k}.`, patch, branding);
1517
+ }
1518
+ };
1519
+ var checkCheckboxGlyph = (issues, stepId, layerId, suffix, g, branding) => {
1520
+ if (!g) return;
1521
+ const p = suffix ? `${suffix}.` : "";
1522
+ checkThemed(issues, stepId, layerId, `${p}background`, g.background, true, branding);
1523
+ checkThemed(issues, stepId, layerId, `${p}border.color`, g.border?.color, false, branding);
1524
+ checkThemed(issues, stepId, layerId, `${p}shadow.color`, g.shadow?.color, false, branding);
1525
+ checkThemed(issues, stepId, layerId, `${p}checkColor`, g.checkColor, false, branding);
1526
+ };
1527
+ var checkCarouselPageControl = (issues, stepId, layerId, pc, branding) => {
1528
+ if (!pc) return;
1529
+ const ind = pc.indicators;
1530
+ if (ind) {
1531
+ checkThemed(issues, stepId, layerId, "pageControl.indicators.defaultColor", ind.defaultColor, false, branding);
1532
+ checkThemed(issues, stepId, layerId, "pageControl.indicators.activeColor", ind.activeColor, false, branding);
1533
+ checkThemed(issues, stepId, layerId, "pageControl.indicators.border.color", ind.border?.color, false, branding);
1534
+ checkThemed(
1535
+ issues,
1536
+ stepId,
1537
+ layerId,
1538
+ "pageControl.indicators.activeBorder.color",
1539
+ ind.activeBorder?.color,
1540
+ false,
1541
+ branding
1542
+ );
1543
+ }
1544
+ checkThemed(issues, stepId, layerId, "pageControl.border.color", pc.border?.color, false, branding);
1545
+ checkThemed(issues, stepId, layerId, "pageControl.shadow.color", pc.shadow?.color, false, branding);
1546
+ };
1547
+ var scanLayer = (issues, screen, layer, branding) => {
1548
+ const stepId = screen.id;
1549
+ const id = layer.id;
1550
+ switch (layer.kind) {
1551
+ case "stack":
1552
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1553
+ checkCommon(issues, stepId, id, "selectedStyle.", layer.selectedStyle, branding);
1554
+ return;
1555
+ case "text":
1556
+ walkTextBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1557
+ return;
1558
+ case "image":
1559
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1560
+ return;
1561
+ case "lottie":
1562
+ case "video":
1563
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1564
+ return;
1565
+ case "icon": {
1566
+ const base = layer.style;
1567
+ checkCommon(issues, stepId, id, "style.", base, branding);
1568
+ checkThemed(issues, stepId, id, "style.color", base?.color, false, branding);
1569
+ const bp = layer.styleBreakpoints;
1570
+ if (bp) {
1571
+ for (const k of BP_KEYS) {
1572
+ const patch = bp[k];
1573
+ if (!patch) continue;
1574
+ checkCommon(issues, stepId, id, `styleBreakpoints.${k}.`, patch, branding);
1575
+ checkThemed(issues, stepId, id, `styleBreakpoints.${k}.color`, patch.color, false, branding);
1576
+ }
1577
+ }
1578
+ return;
1579
+ }
1580
+ case "button":
1581
+ case "back_button": {
1582
+ const base = layer.style;
1583
+ checkTextLike(issues, stepId, id, "style.", base, branding);
1584
+ const bp = layer.styleBreakpoints;
1585
+ if (bp) {
1586
+ for (const k of BP_KEYS) {
1587
+ const patch = bp[k];
1588
+ if (patch) checkTextLike(issues, stepId, id, `styleBreakpoints.${k}.`, patch, branding);
1589
+ }
1590
+ }
1591
+ return;
1592
+ }
1593
+ case "hyperlink":
1594
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1595
+ return;
1596
+ case "progress":
1597
+ checkCommon(issues, stepId, id, "style.", layer.style, branding);
1598
+ checkThemed(issues, stepId, id, "trackColor", layer.trackColor, false, branding);
1599
+ checkThemed(issues, stepId, id, "fillColor", layer.fillColor, false, branding);
1600
+ return;
1601
+ case "loader":
1602
+ checkCommon(issues, stepId, id, "style.", layer.style, branding);
1603
+ checkThemed(issues, stepId, id, "trackColor", layer.trackColor, false, branding);
1604
+ checkThemed(issues, stepId, id, "fillColor", layer.fillColor, false, branding);
1605
+ return;
1606
+ case "counter":
1607
+ walkTextBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1608
+ return;
1609
+ case "checkbox":
1610
+ checkCheckboxGlyph(issues, stepId, id, "uncheckedStyle", layer.uncheckedStyle, branding);
1611
+ checkCheckboxGlyph(issues, stepId, id, "checkedStyle", layer.checkedStyle, branding);
1612
+ return;
1613
+ case "carousel":
1614
+ walkCommonBreakpoints(issues, stepId, id, layer.style, void 0, branding);
1615
+ checkCarouselPageControl(issues, stepId, id, layer.pageControl, branding);
1616
+ return;
1617
+ case "single_choice":
1618
+ case "multiple_choice":
1619
+ return;
1620
+ case "text_input":
1621
+ walkCommonBreakpoints(issues, stepId, id, layer.style, void 0, branding);
1622
+ return;
1623
+ case "scale_input":
1624
+ walkCommonBreakpoints(issues, stepId, id, layer.style, void 0, branding);
1625
+ checkThemed(issues, stepId, id, "labelStyle.color", layer.labelStyle?.color, false, branding);
1626
+ checkThemed(issues, stepId, id, "valueStyle.color", layer.valueStyle?.color, false, branding);
1627
+ checkThemed(issues, stepId, id, "trackColor", layer.trackColor, false, branding);
1628
+ checkThemed(issues, stepId, id, "fillColor", layer.fillColor, false, branding);
1629
+ checkThemed(issues, stepId, id, "thumbColor", layer.thumbColor, false, branding);
1630
+ return;
1631
+ case "oauth_login":
1632
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1633
+ return;
1634
+ case "oauth_provider":
1635
+ if (layer.variant === "preset") {
1636
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1637
+ } else {
1638
+ const base = layer.style;
1639
+ checkTextLike(issues, stepId, id, "style.", base, branding);
1640
+ const bp = layer.styleBreakpoints;
1641
+ if (bp) {
1642
+ for (const k of BP_KEYS) {
1643
+ const patch = bp[k];
1644
+ if (patch) checkTextLike(issues, stepId, id, `styleBreakpoints.${k}.`, patch, branding);
1645
+ }
1646
+ }
1647
+ }
1648
+ return;
1649
+ case "email_password_auth":
1650
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1651
+ return;
1652
+ case "email_password_field":
1653
+ walkCommonBreakpoints(issues, stepId, id, layer.style, layer.styleBreakpoints, branding);
1654
+ return;
1655
+ case "email_password_submit": {
1656
+ const base = layer.style;
1657
+ checkTextLike(issues, stepId, id, "style.", base, branding);
1658
+ const bp = layer.styleBreakpoints;
1659
+ if (bp) {
1660
+ for (const k of BP_KEYS) {
1661
+ const patch = bp[k];
1662
+ if (patch) checkTextLike(issues, stepId, id, `styleBreakpoints.${k}.`, patch, branding);
1663
+ }
1664
+ }
1665
+ return;
1666
+ }
1667
+ default:
1668
+ return;
1669
+ }
1670
+ };
1671
+ var collectBrandGradientManifestIssues = (manifest, branding) => {
1672
+ const issues = [];
1673
+ for (const screen of manifest.screens) {
1674
+ walkScreen(screen, (layer) => scanLayer(issues, screen, layer, branding));
1675
+ }
1676
+ return issues;
1677
+ };
1678
+
1679
+ // src/colorAlpha.ts
1680
+ var multiplyColorAlpha = (color, factor) => {
1681
+ if (color === void 0) return void 0;
1682
+ if (factor === void 0 || factor >= 1 - 1e-6) return color;
1683
+ if (factor <= 1e-6) return "rgba(0,0,0,0)";
1684
+ const hex8 = /^#([0-9a-fA-F]{8})$/;
1685
+ const hex6 = /^#([0-9a-fA-F]{6})$/;
1686
+ const hex3 = /^#([0-9a-fA-F]{3})$/;
1687
+ const rgba = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/;
1688
+ let r;
1689
+ let g;
1690
+ let b;
1691
+ let a = 1;
1692
+ const m8 = color.match(hex8);
1693
+ const m6 = color.match(hex6);
1694
+ const m3 = color.match(hex3);
1695
+ const mr = color.match(rgba);
1696
+ if (m8) {
1697
+ const h = m8[1];
1698
+ r = parseInt(h.slice(0, 2), 16);
1699
+ g = parseInt(h.slice(2, 4), 16);
1700
+ b = parseInt(h.slice(4, 6), 16);
1701
+ a = parseInt(h.slice(6, 8), 16) / 255;
1702
+ } else if (m6) {
1703
+ const h = m6[1];
1704
+ r = parseInt(h.slice(0, 2), 16);
1705
+ g = parseInt(h.slice(2, 4), 16);
1706
+ b = parseInt(h.slice(4, 6), 16);
1707
+ } else if (m3) {
1708
+ const h = m3[1];
1709
+ r = parseInt(h[0] + h[0], 16);
1710
+ g = parseInt(h[1] + h[1], 16);
1711
+ b = parseInt(h[2] + h[2], 16);
1712
+ } else if (mr) {
1713
+ r = Number(mr[1]);
1714
+ g = Number(mr[2]);
1715
+ b = Number(mr[3]);
1716
+ if (mr[4] !== void 0) a = Number(mr[4]);
1717
+ } else {
1718
+ return color;
1719
+ }
1720
+ const outA = Math.min(1, Math.max(0, a * factor));
1721
+ return `rgba(${r},${g},${b},${outA})`;
1722
+ };
1723
+
1724
+ // src/animations.ts
1725
+ var EASING_BEZIERS = {
1726
+ linear: [0, 0, 1, 1],
1727
+ "ease-in": [0.42, 0, 1, 1],
1728
+ "ease-out": [0, 0, 0.58, 1],
1729
+ "ease-in-out": [0.42, 0, 0.58, 1],
1730
+ // Material-style "standard" / "emphasized" curves — useful defaults
1731
+ // for product animations without inviting per-platform divergence.
1732
+ standard: [0.2, 0, 0, 1],
1733
+ emphasized: [0.3, 0, 0, 1]
1734
+ };
1735
+ var sampleBezier = (t, [x1, y1, x2, y2]) => {
1736
+ if (x1 === y1 && x2 === y2) return t;
1737
+ const ax = 3 * x1 - 3 * x2 + 1;
1738
+ const bx = 3 * x2 - 6 * x1;
1739
+ const cx = 3 * x1;
1740
+ const ay = 3 * y1 - 3 * y2 + 1;
1741
+ const by = 3 * y2 - 6 * y1;
1742
+ const cy = 3 * y1;
1743
+ const xAt = (s2) => ((ax * s2 + bx) * s2 + cx) * s2;
1744
+ const yAt = (s2) => ((ay * s2 + by) * s2 + cy) * s2;
1745
+ const dxAt = (s2) => (3 * ax * s2 + 2 * bx) * s2 + cx;
1746
+ let s = t;
1747
+ for (let i = 0; i < 8; i++) {
1748
+ const x = xAt(s) - t;
1749
+ const dx = dxAt(s);
1750
+ if (Math.abs(x) < 1e-6) return yAt(s);
1751
+ if (Math.abs(dx) < 1e-6) break;
1752
+ s -= x / dx;
1753
+ }
1754
+ let lo = 0;
1755
+ let hi = 1;
1756
+ s = t;
1757
+ for (let i = 0; i < 24; i++) {
1758
+ const x = xAt(s);
1759
+ if (Math.abs(x - t) < 1e-6) return yAt(s);
1760
+ if (x < t) lo = s;
1761
+ else hi = s;
1762
+ s = (lo + hi) / 2;
1763
+ }
1764
+ return yAt(s);
1765
+ };
1766
+ var sampleTrack = (track, tNorm) => {
1767
+ const ks = track.keyframes;
1768
+ if (tNorm <= ks[0].t) return ks[0].value;
1769
+ if (tNorm >= ks[ks.length - 1].t) return ks[ks.length - 1].value;
1770
+ let prev = ks[0];
1771
+ let next = ks[ks.length - 1];
1772
+ for (let i = 0; i < ks.length - 1; i++) {
1773
+ if (ks[i].t <= tNorm && ks[i + 1].t >= tNorm) {
1774
+ prev = ks[i];
1775
+ next = ks[i + 1];
1776
+ break;
1777
+ }
1778
+ }
1779
+ const span = next.t - prev.t;
1780
+ const localT = span === 0 ? 0 : (tNorm - prev.t) / span;
1781
+ const eased = sampleBezier(localT, EASING_BEZIERS[prev.easing ?? "linear"]);
1782
+ return prev.value + (next.value - prev.value) * eased;
1783
+ };
1784
+ var effectiveDelayMs = (clip, screen) => {
1785
+ const base = clip.delayMs ?? 0;
1786
+ if (clip.trigger === "stagger" && clip.staggerIndex !== void 0) {
1787
+ const step = screen.stagger?.stepMs ?? 60;
1788
+ return base + clip.staggerIndex * step;
1789
+ }
1790
+ return base;
1791
+ };
1792
+ var sampleClipAt = (clip, screen, tMs) => {
1793
+ const delay = effectiveDelayMs(clip, screen);
1794
+ const local = clip.durationMs > 0 ? (tMs - delay) / clip.durationMs : 1;
1795
+ const tNorm = Math.min(1, Math.max(0, local));
1796
+ const out = {};
1797
+ for (const track of clip.tracks) {
1798
+ out[track.property] = sampleTrack(track, tNorm);
1799
+ }
1800
+ return out;
1801
+ };
1802
+ var mergeSampledClips = (base, overlay) => {
1803
+ const out = { ...base };
1804
+ for (const k of Object.keys(overlay)) {
1805
+ const v = overlay[k];
1806
+ if (v !== void 0) out[k] = v;
1807
+ }
1808
+ return out;
1809
+ };
1810
+ var sampleLayerAnimAt = (screen, layerId, tMs) => {
1811
+ const list = clipsByLayerId(screen).get(layerId) ?? [];
1812
+ if (list.length === 0) return {};
1813
+ const sorted = [...list].sort((a, b) => {
1814
+ const da = effectiveDelayMs(a, screen);
1815
+ const db = effectiveDelayMs(b, screen);
1816
+ if (da !== db) return da - db;
1817
+ return a.id.localeCompare(b.id);
1818
+ });
1819
+ let state = {};
1820
+ for (let i = 0; i < sorted.length; i++) {
1821
+ const clip = sorted[i];
1822
+ const d = effectiveDelayMs(clip, screen);
1823
+ const end = d + clip.durationMs;
1824
+ if (tMs < d) {
1825
+ if (clip.trigger === "unmount") continue;
1826
+ if (clip.trigger !== "mount" && clip.trigger !== "stagger") continue;
1827
+ const earlierAnimating = sorted.slice(0, i).some((earlier) => {
1828
+ const dE = effectiveDelayMs(earlier, screen);
1829
+ const endE = dE + earlier.durationMs;
1830
+ return tMs >= dE && tMs < endE;
1831
+ });
1832
+ if (earlierAnimating) continue;
1833
+ const pending = sampleClipAt(clip, screen, d);
1834
+ state = mergeSampledClips(state, pending);
1835
+ continue;
1836
+ }
1837
+ const at = tMs <= end ? tMs : end;
1838
+ state = mergeSampledClips(state, sampleClipAt(clip, screen, at));
1839
+ }
1840
+ return state;
1841
+ };
1842
+ var layerHasAnimationClips = (screen, layerId) => (screen?.animations ?? []).some((c) => c.targetLayerId === layerId);
1843
+ var screenAnimationsDurationMs = (screen) => {
1844
+ if (!screen.animations || screen.animations.length === 0) return 0;
1845
+ let max = 0;
1846
+ for (const clip of screen.animations) {
1847
+ const total = effectiveDelayMs(clip, screen) + clip.durationMs;
1848
+ if (total > max) max = total;
1849
+ }
1850
+ return max;
1851
+ };
1852
+ var loaderLayerFillTimelineEndMs = (layer) => (layer.fillDelayMs ?? 0) + (layer.durationMs ?? 2e3);
1853
+ var screenLoaderTimelineExtentMs = (screen) => {
1854
+ let max = 0;
1855
+ walkScreen(screen, (l) => {
1856
+ if (l.kind !== "loader") return;
1857
+ const end = loaderLayerFillTimelineEndMs(l);
1858
+ if (end > max) max = end;
1859
+ });
1860
+ return max;
1861
+ };
1862
+ var loaderFillProgressAtGlobalMs = (tMs, fillDelayMs, durationMs) => {
1863
+ if (durationMs <= 0) return tMs >= fillDelayMs ? 1 : 0;
1864
+ return Math.min(1, Math.max(0, (tMs - fillDelayMs) / durationMs));
1865
+ };
1866
+ var DEFAULT_LOTTIE_PLAY_DURATION_MS = 2e3;
1867
+ var lottieJsonDurationMs = (animationData) => {
1868
+ const j = animationData;
1869
+ const fr = typeof j.fr === "number" && j.fr > 0 ? j.fr : 60;
1870
+ const ip = typeof j.ip === "number" ? j.ip : 0;
1871
+ const op = typeof j.op === "number" ? j.op : 60;
1872
+ const frames = Math.max(0, op - ip);
1873
+ return Math.max(1, Math.round(frames / fr * 1e3));
1874
+ };
1875
+ var MIN_ANIMATION_TIMELINE_AUTHORING_SPAN_MS = 1e4;
1876
+ var animationTimelineAuthoringEndMs = (screen) => screenAnimationsDurationMs(screen);
1877
+ var migrateStaggerClipsToMount = (screen) => {
1878
+ const staggerClipPresent = screen.animations?.some((c) => c.trigger === "stagger");
1879
+ const staggerFieldPresent = screen.stagger !== void 0;
1880
+ if (!staggerClipPresent && !staggerFieldPresent) return screen;
1881
+ const step = screen.stagger?.stepMs ?? 60;
1882
+ const { stagger: _st, ...rest } = screen;
1883
+ if (!staggerClipPresent) {
1884
+ return { ...rest };
1885
+ }
1886
+ const nextAnimations = (screen.animations ?? []).map((c) => {
1887
+ if (c.trigger !== "stagger") return c;
1888
+ const base = c.delayMs ?? 0;
1889
+ const extra = (c.staggerIndex ?? 0) * step;
1890
+ return {
1891
+ ...c,
1892
+ trigger: "mount",
1893
+ delayMs: base + extra,
1894
+ staggerIndex: void 0
1895
+ };
1896
+ });
1897
+ return { ...rest, animations: nextAnimations };
1898
+ };
1899
+ var hasStaggerClip = (screen, layerId) => (screen.animations ?? []).some((c) => c.targetLayerId === layerId && c.trigger === "stagger");
1900
+ var applyStaggerIndicesFromTreeOrder = (screen) => {
1901
+ if (!screen.animations?.some((c) => c.trigger === "stagger")) return screen;
1902
+ const staggerIndexByLayerId = /* @__PURE__ */ new Map();
1903
+ const visitStackChildren = (stack) => {
1904
+ let idx = 0;
1905
+ for (const ch of stack.children) {
1906
+ if (hasStaggerClip(screen, ch.id)) {
1907
+ staggerIndexByLayerId.set(ch.id, idx);
1908
+ idx += 1;
1909
+ }
1910
+ visit(ch);
1911
+ }
1912
+ };
1913
+ const visit = (l) => {
1914
+ if (l.kind === "stack") {
1915
+ visitStackChildren(l);
1916
+ return;
1917
+ }
1918
+ if (l.kind === "carousel") {
1919
+ for (const s of l.slides) visit(s);
1920
+ return;
1921
+ }
1922
+ if (l.kind === "button" || l.kind === "back_button") {
1923
+ l.children.forEach(visit);
1924
+ return;
1925
+ }
1926
+ if (l.kind === "hyperlink") {
1927
+ l.children.forEach(visit);
1928
+ return;
1929
+ }
1930
+ if (l.kind === "single_choice" || l.kind === "multiple_choice") {
1931
+ l.children.forEach(visit);
1932
+ return;
1933
+ }
1934
+ if (l.kind === "text_input" || l.kind === "scale_input") {
1935
+ l.children?.forEach(visit);
1936
+ return;
1937
+ }
1938
+ if (l.kind === "oauth_login") {
1939
+ l.children.forEach(visit);
1940
+ return;
1941
+ }
1942
+ if (l.kind === "oauth_provider" && l.variant === "custom") {
1943
+ l.children.forEach(visit);
1944
+ return;
1945
+ }
1946
+ if (l.kind === "email_password_auth") {
1947
+ l.children.forEach(visit);
1948
+ return;
1949
+ }
1950
+ if (l.kind === "email_password_field") {
1951
+ l.children?.forEach(visit);
1952
+ return;
1953
+ }
1954
+ if (l.kind === "email_password_submit") {
1955
+ l.children.forEach(visit);
1956
+ return;
1957
+ }
1958
+ };
1959
+ if (screen.regions.header) visit(screen.regions.header);
1960
+ visit(screen.regions.body);
1961
+ if (screen.regions.footer) visit(screen.regions.footer);
1962
+ const nextAnimations = screen.animations.map((c) => {
1963
+ if (c.trigger !== "stagger") return c;
1964
+ const nextIdx = staggerIndexByLayerId.get(c.targetLayerId);
1965
+ if (nextIdx === void 0) return c;
1966
+ if (c.staggerIndex === nextIdx) return c;
1967
+ return { ...c, staggerIndex: nextIdx };
1968
+ });
1969
+ if (nextAnimations.every((c, i) => c === screen.animations[i])) return screen;
1970
+ return { ...screen, animations: nextAnimations };
1971
+ };
1972
+ var clipsByLayerId = (screen) => {
1973
+ const map = /* @__PURE__ */ new Map();
1974
+ if (!screen.animations) return map;
1975
+ for (const clip of screen.animations) {
1976
+ const list = map.get(clip.targetLayerId) ?? [];
1977
+ list.push(clip);
1978
+ map.set(clip.targetLayerId, list);
1979
+ }
1980
+ return map;
1981
+ };
1982
+ var applyReducedMotion = (clip, policy) => {
1983
+ if (policy === "play") return clip;
1984
+ return {
1985
+ ...clip,
1986
+ durationMs: 0,
1987
+ delayMs: 0,
1988
+ tracks: clip.tracks.map((t) => ({
1989
+ ...t,
1990
+ keyframes: t.keyframes.map((k) => ({ ...k, t: k.t === 0 ? 0 : 1 }))
1991
+ }))
1992
+ };
1993
+ };
1994
+ var listAnimatablePropsForLayerKind = (_kind) => ["opacity", "translateX", "translateY", "scale"];
1995
+ var isPropertyAllowedOnLayer = (layer, property) => listAnimatablePropsForLayerKind(layer.kind).includes(property);
1996
+
1997
+ // src/restingMotion/restingMotionEntries.ts
1998
+ var RESTING_MOTION_DEFAULT_DURATION_MS = {
1999
+ translate: 2400,
2000
+ bounce: 2e3,
2001
+ scale: 2200,
2002
+ pulse: 1800,
2003
+ rotate: 3200
2004
+ };
2005
+ var RESTING_MOTION_SEGMENT_DURATION_MS_MIN = 200;
2006
+ var RESTING_MOTION_SEGMENT_DURATION_MS_MAX = 2e4;
2007
+ var clampRestingMotionSegmentDurationMs = (ms) => Math.min(
2008
+ RESTING_MOTION_SEGMENT_DURATION_MS_MAX,
2009
+ Math.max(RESTING_MOTION_SEGMENT_DURATION_MS_MIN, Math.round(ms))
2010
+ );
2011
+ var LEGACY_RESTING_MOTION_ID = "__legacy";
2012
+ var layerRestingMotionEntries = (layer) => {
2013
+ const list = layer.restingMotions;
2014
+ if (list && list.length > 0) return list;
2015
+ if (layer.restingMotion) {
2016
+ return [{ ...layer.restingMotion, id: LEGACY_RESTING_MOTION_ID }];
2017
+ }
2018
+ return [];
2019
+ };
2020
+ var layerWithRestingMotionEntries = (layer, entries) => {
2021
+ if (entries.length === 0) {
2022
+ const next = { ...layer };
2023
+ delete next.restingMotion;
2024
+ delete next.restingMotions;
2025
+ return next;
2026
+ }
2027
+ return {
2028
+ ...layer,
2029
+ restingMotions: entries.map((e) => ({ ...e })),
2030
+ restingMotion: void 0
2031
+ };
2032
+ };
2033
+ var restingMotionSyncScaleNonLoopClipAndPattern = (r, ms) => {
2034
+ const v = clampRestingMotionSegmentDurationMs(ms);
2035
+ return { ...r, durationMs: v, scalePatternDurationMs: v };
2036
+ };
2037
+ var restingMotionNormalizeScaleNonLoopClip = (r) => {
2038
+ if (r.preset !== "scale" || r.loop === true) return r;
2039
+ const ms = clampRestingMotionSegmentDurationMs(
2040
+ r.durationMs ?? r.scalePatternDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS.scale
2041
+ );
2042
+ return { ...r, durationMs: ms, scalePatternDurationMs: ms };
2043
+ };
2044
+ var restingMotionEffectiveDurationMs = (r) => r.durationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];
2045
+ var restingMotionCycleDurationMs = (r) => r.cycleDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];
2046
+ var restingMotionMotionSpeedSliderMs = (r) => r.loop === true ? restingMotionCycleDurationMs(r) : restingMotionEffectiveDurationMs(r);
2047
+ var restingMotionIntensity = (r) => {
2048
+ "worklet";
2049
+ return r.intensity ?? 1;
2050
+ };
2051
+ var restingMotionDelayAfterMountEndMs = (r) => r.delayMsAfterMountEnd ?? 0;
2052
+
2053
+ // src/restingMotion/restingMotionTimeline.ts
2054
+ var layerMountClipsEndMs = (screen, layerId) => {
2055
+ const clips = screen.animations?.filter(
2056
+ (c) => c.targetLayerId === layerId && (c.trigger === "mount" || c.trigger === "stagger")
2057
+ ) ?? [];
2058
+ let maxEnd = 0;
2059
+ for (const c of clips) {
2060
+ maxEnd = Math.max(maxEnd, effectiveDelayMs(c, screen) + c.durationMs);
2061
+ }
2062
+ return maxEnd;
2063
+ };
2064
+ var layerRestingMotionStartMs = (screen, layerId, cfg) => cfg.timelineStartMs !== void 0 ? cfg.timelineStartMs : layerMountClipsEndMs(screen, layerId) + restingMotionDelayAfterMountEndMs(cfg);
2065
+ var screenRestingTimelineExtentMs = (screen) => {
2066
+ let max = 0;
2067
+ walkScreen(screen, (l) => {
2068
+ for (const r of layerRestingMotionEntries(l)) {
2069
+ const start = layerRestingMotionStartMs(screen, l.id, r);
2070
+ max = Math.max(max, start + restingMotionEffectiveDurationMs(r));
2071
+ }
2072
+ });
2073
+ return max;
2074
+ };
2075
+ var motionTimelineScrubClampMs = (screen) => Math.max(
2076
+ screenAnimationsDurationMs(screen),
2077
+ screenRestingTimelineExtentMs(screen),
2078
+ screenLoaderTimelineExtentMs(screen)
2079
+ );
2080
+ var DEFAULT_WORKBENCH_TIMELINE_MS = 1e4;
2081
+ var workbenchTimelineTotalMs = (screen) => {
2082
+ const extent = motionTimelineScrubClampMs(screen);
2083
+ return extent > 0 ? extent : DEFAULT_WORKBENCH_TIMELINE_MS;
2084
+ };
2085
+ var layerRestingMotionEndMs = (screen, layerId, cfg) => layerRestingMotionStartMs(screen, layerId, cfg) + restingMotionEffectiveDurationMs(cfg);
2086
+ var restingMotionAllowedAtTime = (screen, layerId, tMs, cfg) => {
2087
+ const start = layerRestingMotionStartMs(screen, layerId, cfg);
2088
+ const end = start + restingMotionEffectiveDurationMs(cfg);
2089
+ return tMs >= start && tMs < end;
2090
+ };
2091
+ var restingMotionLocalElapsedMs = (screen, layerId, cfg, tMs) => {
2092
+ if (!restingMotionAllowedAtTime(screen, layerId, tMs, cfg)) return null;
2093
+ return tMs - layerRestingMotionStartMs(screen, layerId, cfg);
2094
+ };
2095
+
2096
+ // src/restingMotion/restingMotionEffects.ts
2097
+ var RESTING_MOTION_BOUNCE_BASE_PX = 14;
2098
+ var restingMotionBounceAmplitudePx = (r) => r.bounceAmplitudePx ?? RESTING_MOTION_BOUNCE_BASE_PX * restingMotionIntensity(r);
2099
+ var bouncePhaseToTranslateY = (ph, amplitudePx) => -amplitudePx * Math.sin(Math.PI * ph);
2100
+ var RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT = 8;
2101
+ var RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT = 0;
2102
+ var RESTING_MOTION_DEFAULT_SCALE_PERCENT = RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;
2103
+ var RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT = 6;
2104
+ var RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX = 6;
2105
+ var RESTING_MOTION_DEFAULT_TRANSLATE_RANGE_PX = RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX;
2106
+ var RESTING_MOTION_DEFAULT_ROTATE_DEG = 5;
2107
+ var RESTING_MOTION_PULSE_DIP_BASE = 0.38;
2108
+ var restingMotionScalePatternDurationMs = (r) => {
2109
+ const def = RESTING_MOTION_DEFAULT_DURATION_MS.scale;
2110
+ if (r.preset !== "scale") return def;
2111
+ if (r.loop !== true) {
2112
+ return r.durationMs ?? r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;
2113
+ }
2114
+ return r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;
2115
+ };
2116
+ var restingMotionPhase01 = (cfg, localMs) => {
2117
+ const segment = restingMotionEffectiveDurationMs(cfg);
2118
+ const cycle = restingMotionCycleDurationMs(cfg);
2119
+ if (segment <= 0) return 0;
2120
+ if (cfg.preset === "scale") {
2121
+ const pattern = restingMotionScalePatternDurationMs(cfg);
2122
+ if (pattern <= 0) return 0;
2123
+ if (cfg.loop === true) {
2124
+ const u = (localMs % pattern + pattern) % pattern;
2125
+ return u / pattern;
2126
+ }
2127
+ return Math.min(1, Math.max(0, localMs / pattern));
2128
+ }
2129
+ if (cfg.loop === true && cycle > 0) {
2130
+ const u = (localMs % cycle + cycle) % cycle;
2131
+ return u / cycle;
2132
+ }
2133
+ return Math.min(1, Math.max(0, localMs / segment));
2134
+ };
2135
+ var restingMotionScaleDirection = (r) => {
2136
+ "worklet";
2137
+ if (r.scaleDirection === "down") return "down";
2138
+ if (r.scaleDirection === "up") return "up";
2139
+ const down = r.scaleDownPercent ?? 0;
2140
+ const up = r.scaleUpPercent ?? 0;
2141
+ if (down > 0 && up === 0) return "down";
2142
+ return "up";
2143
+ };
2144
+ var restingMotionScalePercentResolved = (r) => {
2145
+ "worklet";
2146
+ if (r.scalePercent !== void 0) return r.scalePercent;
2147
+ if (restingMotionScaleDirection(r) === "down") {
2148
+ return r.scaleDownPercent !== void 0 ? r.scaleDownPercent : r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT;
2149
+ }
2150
+ return r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;
2151
+ };
2152
+ var restingMotionScaleAmountFraction = (r) => {
2153
+ "worklet";
2154
+ return restingMotionScalePercentResolved(r) / 100 * restingMotionIntensity(r);
2155
+ };
2156
+ var restingMotionScaleSpringBack = (r) => {
2157
+ "worklet";
2158
+ return r.scaleSpringBack !== false;
2159
+ };
2160
+ var restingMotionScaleUpFraction = (r) => restingMotionScaleDirection(r) === "up" ? restingMotionScaleAmountFraction(r) : 0;
2161
+ var restingMotionScaleDownFraction = (r) => restingMotionScaleDirection(r) === "down" ? restingMotionScaleAmountFraction(r) : 0;
2162
+ var restingMotionScalePeakMultiplier = (cfg) => {
2163
+ "worklet";
2164
+ const amt = restingMotionScaleAmountFraction(cfg);
2165
+ const dir = restingMotionScaleDirection(cfg);
2166
+ return dir === "up" ? 1 + amt : 1 - amt;
2167
+ };
2168
+ var restingMotionScaleAtPhase = (cfg, ph) => {
2169
+ "worklet";
2170
+ const peak = restingMotionScalePeakMultiplier(cfg);
2171
+ const p = Math.min(1, Math.max(0, ph));
2172
+ if (!restingMotionScaleSpringBack(cfg)) {
2173
+ return 1 + (peak - 1) * p;
2174
+ }
2175
+ if (p < 0.5) {
2176
+ const t2 = p * 2;
2177
+ return 1 + (peak - 1) * t2;
2178
+ }
2179
+ const t = (p - 0.5) * 2;
2180
+ return peak + (1 - peak) * t;
2181
+ };
2182
+ var legacyTranslatePxToDisplayPercent = (px) => Math.max(-200, Math.min(200, Math.round(px / 80 * 200)));
2183
+ var restingMotionTranslateBasePeak = (r) => {
2184
+ "worklet";
2185
+ const hasPercent = r.translatePeakXPercent !== void 0 || r.translatePeakYPercent !== void 0;
2186
+ const hasPx = r.translatePeakXPx !== void 0 || r.translatePeakYPx !== void 0;
2187
+ const hasLegacyRange = r.translateRangePx !== void 0;
2188
+ if (hasPercent) {
2189
+ return {
2190
+ unit: "percent",
2191
+ x: r.translatePeakXPercent ?? 0,
2192
+ y: r.translatePeakYPercent ?? 0
2193
+ };
2194
+ }
2195
+ if (hasPx || hasLegacyRange) {
2196
+ if (hasPx) {
2197
+ return {
2198
+ unit: "px",
2199
+ x: r.translatePeakXPx ?? 0,
2200
+ y: r.translatePeakYPx ?? 0
2201
+ };
2202
+ }
2203
+ return { unit: "px", x: 0, y: r.translateRangePx ?? 0 };
2204
+ }
2205
+ return { unit: "percent", x: 0, y: RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT };
2206
+ };
2207
+ var restingMotionTranslateAuthoringPeakPercent = (r) => {
2208
+ const base = restingMotionTranslateBasePeak(r);
2209
+ if (base.unit === "percent") return { x: base.x, y: base.y };
2210
+ return {
2211
+ x: legacyTranslatePxToDisplayPercent(base.x),
2212
+ y: legacyTranslatePxToDisplayPercent(base.y)
2213
+ };
2214
+ };
2215
+ var restingMotionTranslateAuthoringPeakPx = (r) => {
2216
+ const base = restingMotionTranslateBasePeak(r);
2217
+ if (base.unit === "px") return { x: base.x, y: base.y };
2218
+ return { x: 0, y: 0 };
2219
+ };
2220
+ var restingMotionTranslatePeakResolved = (r) => {
2221
+ "worklet";
2222
+ const base = restingMotionTranslateBasePeak(r);
2223
+ const i = restingMotionIntensity(r);
2224
+ if (base.unit === "percent") {
2225
+ let x2 = base.x * i;
2226
+ let y2 = base.y * i;
2227
+ x2 = Math.max(-200, Math.min(200, x2));
2228
+ y2 = Math.max(-200, Math.min(200, y2));
2229
+ return { unit: "percent", x: x2, y: y2 };
2230
+ }
2231
+ let x = base.x * i;
2232
+ let y = base.y * i;
2233
+ x = Math.max(-200, Math.min(200, x));
2234
+ y = Math.max(-200, Math.min(200, y));
2235
+ return { unit: "px", x, y };
2236
+ };
2237
+ var restingMotionTranslatePeakPx = (r) => {
2238
+ const r2 = restingMotionTranslatePeakResolved(r);
2239
+ return { x: r2.x, y: r2.y };
2240
+ };
2241
+ var restingMotionTranslateSpringBack = (r) => {
2242
+ "worklet";
2243
+ return r.translateSpringBack !== false;
2244
+ };
2245
+ var restingMotionRotateMaxDeg = (r) => {
2246
+ "worklet";
2247
+ const raw = (r.rotateMaxDeg ?? RESTING_MOTION_DEFAULT_ROTATE_DEG) * restingMotionIntensity(r);
2248
+ return Math.max(0, Math.min(360, raw));
2249
+ };
2250
+ var restingMotionRotateSpringBack = (r) => {
2251
+ "worklet";
2252
+ return r.rotateSpringBack !== false;
2253
+ };
2254
+ var restingMotionRotateDirection = (r) => {
2255
+ "worklet";
2256
+ return r.rotateDirection === "counterclockwise" ? "counterclockwise" : "clockwise";
2257
+ };
2258
+ var restingMotionRotateSign = (r) => {
2259
+ "worklet";
2260
+ return restingMotionRotateDirection(r) === "counterclockwise" ? -1 : 1;
2261
+ };
2262
+ var restingMotionPulseMinOpacity = (r) => {
2263
+ "worklet";
2264
+ if (r.pulseMinOpacity !== void 0) {
2265
+ return Math.max(0, Math.min(1, r.pulseMinOpacity));
2266
+ }
2267
+ return Math.max(
2268
+ 0,
2269
+ Math.min(1, 1 - RESTING_MOTION_PULSE_DIP_BASE * restingMotionIntensity(r))
2270
+ );
2271
+ };
2272
+ var restingMotionSampleStyle = (cfg, ph) => {
2273
+ switch (cfg.preset) {
2274
+ case "translate": {
2275
+ const peak = restingMotionTranslatePeakResolved(cfg);
2276
+ const env = restingMotionTranslateSpringBack(cfg) ? Math.sin(ph * Math.PI) : ph;
2277
+ let tx = env * peak.x;
2278
+ let ty = env * peak.y;
2279
+ if (Math.abs(tx) < 1e-6) tx = 0;
2280
+ if (Math.abs(ty) < 1e-6) ty = 0;
2281
+ if (peak.unit === "percent") {
2282
+ return { transform: `translate(${tx}%, ${ty}%)`, willChange: "transform" };
2283
+ }
2284
+ return { transform: `translate(${tx}px, ${ty}px)`, willChange: "transform" };
2285
+ }
2286
+ case "bounce": {
2287
+ const y = bouncePhaseToTranslateY(ph, restingMotionBounceAmplitudePx(cfg));
2288
+ return { transform: `translateY(${y}px)`, willChange: "transform" };
2289
+ }
2290
+ case "scale": {
2291
+ const sc = restingMotionScaleAtPhase(cfg, ph);
2292
+ return { transform: `scale(${sc})`, willChange: "transform" };
2293
+ }
2294
+ case "pulse": {
2295
+ const omin = restingMotionPulseMinOpacity(cfg);
2296
+ const dip = 1 - omin;
2297
+ const op = ph <= 0.5 ? 1 - ph * 2 * dip : 1 - (1 - ph) * 2 * dip;
2298
+ return { opacity: op, willChange: "opacity" };
2299
+ }
2300
+ case "rotate": {
2301
+ const peakDeg = restingMotionRotateMaxDeg(cfg);
2302
+ let deg = restingMotionRotateSign(cfg) * (restingMotionRotateSpringBack(cfg) ? Math.sin(ph * Math.PI) * peakDeg : ph * peakDeg);
2303
+ if (Math.abs(deg) < 1e-6) deg = 0;
2304
+ return { transform: `rotate(${deg}deg)`, willChange: "transform" };
2305
+ }
2306
+ default:
2307
+ return {};
2308
+ }
2309
+ };
2310
+ var restingMotionStyleAtTime = (screen, layerId, cfg, tMs) => {
2311
+ const local = restingMotionLocalElapsedMs(screen, layerId, cfg, tMs);
2312
+ if (local === null) return null;
2313
+ const ph = restingMotionPhase01(cfg, local);
2314
+ return restingMotionSampleStyle(cfg, ph);
2315
+ };
2316
+ var RESTING_MOTION_KEYFRAMES_CSS = `
2317
+ @keyframes ob-rm-translate {
2318
+ 0%, 100% { transform: translate(0, 0); }
2319
+ 50% {
2320
+ transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));
2321
+ }
2322
+ }
2323
+ @keyframes ob-rm-translate-ramp {
2324
+ 0% { transform: translate(0, 0); }
2325
+ 100% {
2326
+ transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));
2327
+ }
2328
+ }
2329
+ @keyframes ob-rm-bounce {
2330
+ 0%, 100% { transform: translateY(0); }
2331
+ 50% { transform: translateY(calc(-1 * var(--ob-rm-bounce-px, 14px))); }
2332
+ }
2333
+ @keyframes ob-rm-scale {
2334
+ 0%, 100% { transform: scale(1); }
2335
+ 50% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }
2336
+ }
2337
+ @keyframes ob-rm-scale-ramp {
2338
+ 0% { transform: scale(1); }
2339
+ 100% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }
2340
+ }
2341
+ @keyframes ob-rm-pulse {
2342
+ 0%, 100% { opacity: 1; }
2343
+ 50% { opacity: var(--ob-rm-pulse-min, 0.62); }
2344
+ }
2345
+ @keyframes ob-rm-rotate {
2346
+ 0%, 100% { transform: rotate(0deg); }
2347
+ 50% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }
2348
+ }
2349
+ @keyframes ob-rm-rotate-ramp {
2350
+ 0% { transform: rotate(0deg); }
2351
+ 100% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }
2352
+ }
2353
+ `.trim();
2354
+ var restingMotionWebAnimationName = (config) => {
2355
+ if (config.preset === "rotate" && !restingMotionRotateSpringBack(config)) {
2356
+ return "ob-rm-rotate-ramp";
2357
+ }
2358
+ if (config.preset === "translate" && !restingMotionTranslateSpringBack(config)) {
2359
+ return "ob-rm-translate-ramp";
2360
+ }
2361
+ if (config.preset === "scale" && !restingMotionScaleSpringBack(config)) {
2362
+ return "ob-rm-scale-ramp";
2363
+ }
2364
+ return `ob-rm-${config.preset}`;
2365
+ };
2366
+ var restingMotionWebStyle = (config) => {
2367
+ const duration = restingMotionEffectiveDurationMs(config);
2368
+ const intensity = restingMotionIntensity(config);
2369
+ const name = restingMotionWebAnimationName(config);
2370
+ const oneCycleMs = config.preset === "scale" ? restingMotionScalePatternDurationMs(config) : config.loop === true ? restingMotionCycleDurationMs(config) : duration;
2371
+ const iter = config.loop === true ? "infinite" : "1 forwards";
2372
+ const base = {
2373
+ ["--ob-rm-i"]: String(intensity),
2374
+ animation: `${name} ${oneCycleMs}ms ease-in-out ${iter}`,
2375
+ willChange: config.preset === "pulse" ? "opacity" : "transform"
2376
+ };
2377
+ if (config.preset === "bounce") {
2378
+ return {
2379
+ ...base,
2380
+ ["--ob-rm-bounce-px"]: `${restingMotionBounceAmplitudePx(config)}px`
2381
+ };
2382
+ }
2383
+ if (config.preset === "scale") {
2384
+ return {
2385
+ ...base,
2386
+ ["--ob-rm-scale-peak"]: String(restingMotionScalePeakMultiplier(config))
2387
+ };
2388
+ }
2389
+ if (config.preset === "translate") {
2390
+ const peak = restingMotionTranslatePeakResolved(config);
2391
+ const sx = peak.unit === "percent" ? `${peak.x}%` : `${peak.x}px`;
2392
+ const sy = peak.unit === "percent" ? `${peak.y}%` : `${peak.y}px`;
2393
+ return {
2394
+ ...base,
2395
+ ["--ob-rm-translate-peak-x"]: sx,
2396
+ ["--ob-rm-translate-peak-y"]: sy
2397
+ };
2398
+ }
2399
+ if (config.preset === "rotate") {
2400
+ const signedPeak = restingMotionRotateSign(config) * restingMotionRotateMaxDeg(config);
2401
+ return {
2402
+ ...base,
2403
+ ["--ob-rm-rotate-peak"]: `${signedPeak}deg`
2404
+ };
2405
+ }
2406
+ if (config.preset === "pulse") {
2407
+ return {
2408
+ ...base,
2409
+ ["--ob-rm-pulse-min"]: String(restingMotionPulseMinOpacity(config))
2410
+ };
2411
+ }
2412
+ return base;
2413
+ };
2414
+
2415
+ // src/dropShadow.ts
2416
+ var clamp01 = (n) => Math.max(0, Math.min(1, n));
2417
+ var hexToRgba = (hex, alpha) => {
2418
+ const raw = hex.trim().replace(/^#/, "");
2419
+ if (!/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(raw)) return `rgba(0,0,0,${clamp01(alpha)})`;
2420
+ const full = raw.length === 3 ? raw.split("").map((c) => c + c).join("") : raw;
2421
+ const r = parseInt(full.slice(0, 2), 16);
2422
+ const g = parseInt(full.slice(2, 4), 16);
2423
+ const b = parseInt(full.slice(4, 6), 16);
2424
+ return `rgba(${r},${g},${b},${clamp01(alpha)})`;
2425
+ };
2426
+ var colorWithOpacity = (resolved, opacity) => {
2427
+ const o = clamp01(opacity);
2428
+ if (!resolved) return `rgba(0,0,0,${o})`;
2429
+ const t = resolved.trim();
2430
+ if (t.startsWith("#")) return hexToRgba(t, o);
2431
+ const m = t.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
2432
+ if (m) return `rgba(${m[1]},${m[2]},${m[3]},${o})`;
2433
+ return t;
2434
+ };
2435
+ var shadowHasAnyField = (sh) => sh !== void 0 && Object.values(sh).some((v) => v !== void 0);
2436
+ var dropShadowToBoxShadow = (sh, theme, palette) => {
2437
+ if (!shadowHasAnyField(sh)) return void 0;
2438
+ const ox = sh.offsetX ?? 0;
2439
+ const oy = sh.offsetY ?? 2;
2440
+ const blur = sh.blur ?? 8;
2441
+ const spread = sh.spread ?? 0;
2442
+ const opacity = sh.opacity ?? 0.25;
2443
+ const base = resolveThemedColor(theme, palette, sh.color);
2444
+ const color = colorWithOpacity(base, opacity);
2445
+ return `${ox}px ${oy}px ${blur}px ${spread}px ${color}`;
2446
+ };
2447
+ var dropShadowToWebStyle = (sh, theme, palette) => {
2448
+ const boxShadow = dropShadowToBoxShadow(sh, theme, palette);
2449
+ return boxShadow ? { boxShadow } : {};
2450
+ };
2451
+ var dropShadowToNativeStyle = (sh, theme, palette) => {
2452
+ if (!shadowHasAnyField(sh)) return {};
2453
+ const ox = sh.offsetX ?? 0;
2454
+ const oy = sh.offsetY ?? 2;
2455
+ const blur = sh.blur ?? 8;
2456
+ const opacity = sh.opacity ?? 0.25;
2457
+ const base = resolveThemedColor(theme, palette, sh.color);
2458
+ const color = colorWithOpacity(base, opacity);
2459
+ return {
2460
+ shadowOffset: { width: ox, height: oy },
2461
+ shadowOpacity: 1,
2462
+ shadowRadius: blur,
2463
+ shadowColor: color,
2464
+ elevation: Math.min(24, Math.max(0, Math.round(blur / 2 + Math.abs(oy))))
2465
+ };
2466
+ };
2467
+
2468
+ // src/scaleValidation.ts
2469
+ var scaleStep = (layer) => layer.step ?? 1;
2470
+ var snapScaleValue = (layer, raw) => {
2471
+ const step = scaleStep(layer);
2472
+ const { min, max } = layer;
2473
+ const n = Math.round((raw - min) / step);
2474
+ const v = min + n * step;
2475
+ if (v < min) return min;
2476
+ if (v > max) return max;
2477
+ return v;
2478
+ };
2479
+ var scaleValueIsOnStep = (layer, value) => {
2480
+ const step = scaleStep(layer);
2481
+ const n = (value - layer.min) / step;
2482
+ return Number.isFinite(n) && Math.abs(n - Math.round(n)) < 1e-6;
2483
+ };
2484
+ var scaleValueInRange = (layer, value) => value >= layer.min && value <= layer.max;
2485
+
2486
+ // src/textInputValidation.ts
2487
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
2488
+ var PHONE_RE = /^[\d\s+().-]{3,32}$/u;
2489
+ var isProbablyUrl = (s) => {
2490
+ try {
2491
+ const withProto = /^[a-z]+:/iu.test(s) ? s : `https://${s}`;
2492
+ const u = new URL(withProto);
2493
+ return u.hostname.length > 0;
2494
+ } catch {
2495
+ return false;
2496
+ }
2497
+ };
2498
+ var effectiveInputType = (layer) => layer.inputType ?? "plain";
2499
+ var effectiveRequired = (layer) => layer.required !== false;
2500
+ var validateTextInputValue = (layer, raw) => {
2501
+ const trimmed = raw.trim();
2502
+ const required = effectiveRequired(layer);
2503
+ if (trimmed.length === 0) {
2504
+ if (!required) return { ok: true };
2505
+ return { ok: false, reason: "Text is empty" };
2506
+ }
2507
+ if (layer.minLength !== void 0 && trimmed.length < layer.minLength) {
2508
+ return { ok: false, reason: `Enter at least ${layer.minLength} characters` };
2509
+ }
2510
+ if (layer.maxLength !== void 0 && trimmed.length > layer.maxLength) {
2511
+ return { ok: false, reason: `Exceeds max length of ${layer.maxLength}` };
2512
+ }
2513
+ const mode = effectiveInputType(layer);
2514
+ switch (mode) {
2515
+ case "plain":
2516
+ case "multiline":
2517
+ return { ok: true };
2518
+ case "email":
2519
+ return EMAIL_RE.test(trimmed) ? { ok: true } : { ok: false, reason: "Enter a valid email address" };
2520
+ case "phone":
2521
+ return PHONE_RE.test(trimmed) ? { ok: true } : { ok: false, reason: "Enter a valid phone number (digits and common symbols only)" };
2522
+ case "url":
2523
+ return isProbablyUrl(trimmed) ? { ok: true } : { ok: false, reason: "Enter a valid URL" };
2524
+ default:
2525
+ return { ok: true };
2526
+ }
2527
+ };
2528
+
2529
+ // src/emailPasswordAuthValidation.ts
2530
+ var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2531
+ var validateEmailPasswordAuthFields = (args) => {
2532
+ const email = args.email.trim();
2533
+ if (!email) return { ok: false, message: "Email is required" };
2534
+ if (!EMAIL_RE2.test(email)) return { ok: false, message: "Enter a valid email" };
2535
+ if (!args.password) return { ok: false, message: "Password is required" };
2536
+ if (args.password.length < args.minPasswordLength) {
2537
+ return {
2538
+ ok: false,
2539
+ message: `Password must be at least ${args.minPasswordLength} characters`
2540
+ };
2541
+ }
2542
+ if (args.mode === "sign_up") {
2543
+ if (!args.confirmPassword) return { ok: false, message: "Confirm your password" };
2544
+ if (args.password !== args.confirmPassword) {
2545
+ return { ok: false, message: "Passwords do not match" };
2546
+ }
2547
+ }
2548
+ return { ok: true };
2549
+ };
2550
+ var extractLiquidTemplateBodies = (s) => {
2551
+ const out = [];
2552
+ let i = 0;
2553
+ while (i < s.length) {
2554
+ const open = s.indexOf("{{", i);
2555
+ if (open === -1) break;
2556
+ const close = s.indexOf("}}", open + 2);
2557
+ if (close === -1) break;
2558
+ out.push(s.slice(open + 2, close));
2559
+ i = close + 2;
2560
+ }
2561
+ return out;
2562
+ };
2563
+ var FIELD_KEY_SOURCE = FIELD_KEY_RE$1.source.replace(/^\^|\$$/g, "");
2564
+ var parseDefaultFilter = (inner) => {
2565
+ const m = inner.match(/\s*\|\s*default\s*:/i);
2566
+ if (!m || m.index === void 0) return { exprPart: inner.trim() };
2567
+ const exprPart = inner.slice(0, m.index).trim();
2568
+ let tail = inner.slice(m.index + m[0].length).trim();
2569
+ if (tail.startsWith('"')) {
2570
+ const end = tail.indexOf('"', 1);
2571
+ if (end > 0) tail = tail.slice(1, end);
2572
+ else tail = tail.slice(1);
2573
+ }
2574
+ return { exprPart, defaultValue: tail };
2575
+ };
2576
+ var parseExpression = (exprRaw) => {
2577
+ const expr = exprRaw.trim();
2578
+ const customM = expr.match(new RegExp(`^custom\\.(${FIELD_KEY_SOURCE})$`));
2579
+ if (customM) return { kind: "custom", key: customM[1] };
2580
+ const idM = expr.match(new RegExp(`^(${FIELD_KEY_SOURCE})\\.id$`));
2581
+ if (idM) return { kind: "field", fieldKey: idM[1], mode: "id" };
2582
+ const plainM = expr.match(new RegExp(`^(${FIELD_KEY_SOURCE})$`));
2583
+ if (plainM) return { kind: "field", fieldKey: plainM[1], mode: "label" };
2584
+ return { kind: "invalid" };
2585
+ };
2586
+ var analyzeLiquidTemplateInner = (inner) => {
2587
+ const { exprPart, defaultValue } = parseDefaultFilter(inner);
2588
+ const p = parseExpression(exprPart);
2589
+ if (p.kind === "invalid") return { expr: { kind: "invalid" }, defaultValue };
2590
+ if (p.kind === "custom") return { expr: { kind: "custom", key: p.key }, defaultValue };
2591
+ return { expr: { kind: "field", fieldKey: p.fieldKey, mode: p.mode }, defaultValue };
2592
+ };
2593
+ var findInputOwner = (manifest, fieldKey) => {
2594
+ for (const screen of manifest.screens) {
2595
+ const input = findInputLayer(screen);
2596
+ if (input && input.fieldKey === fieldKey) return { screen, input };
2597
+ }
2598
+ return null;
2599
+ };
2600
+ var choiceLabelFor = (manifest, fieldKey, choiceId, locale) => {
2601
+ const owner = findInputOwner(manifest, fieldKey);
2602
+ if (!owner) return "";
2603
+ const { input } = owner;
2604
+ if (input.kind !== "single_choice" && input.kind !== "multiple_choice") return "";
2605
+ return choiceOptionLabel(input, choiceId, locale);
2606
+ };
2607
+ var responseToDisplayString = (manifest, fieldKey, response, mode, locale) => {
2608
+ if (!response) return "";
2609
+ switch (response.kind) {
2610
+ case "text":
2611
+ return mode === "id" ? "" : response.value;
2612
+ case "scale":
2613
+ return mode === "id" ? "" : String(response.value);
2614
+ case "choice":
2615
+ if (mode === "id") return response.choiceId;
2616
+ return choiceLabelFor(manifest, fieldKey, response.choiceId, locale);
2617
+ case "multiChoice":
2618
+ if (mode === "id") return response.choiceIds.join(", ");
2619
+ return response.choiceIds.map((id) => choiceLabelFor(manifest, fieldKey, id, locale)).filter(Boolean).join(", ");
2620
+ case "checkbox":
2621
+ return mode === "id" ? "" : response.value ? "true" : "false";
2622
+ case "bypass_input":
2623
+ return "";
2624
+ default:
2625
+ return "";
2626
+ }
2627
+ };
2628
+ var evalParsed = (parsed, ctx) => {
2629
+ if (parsed.kind === "invalid") return "";
2630
+ if (parsed.kind === "custom") {
2631
+ const v = ctx.customProperties?.[parsed.key];
2632
+ return v === void 0 || v === "" ? "" : String(v);
2633
+ }
2634
+ const owner = findInputOwner(ctx.manifest, parsed.fieldKey);
2635
+ const r = ctx.responses[parsed.fieldKey];
2636
+ if (parsed.mode === "id" && owner && owner.input.kind !== "single_choice" && owner.input.kind !== "multiple_choice") {
2637
+ return "";
2638
+ }
2639
+ return responseToDisplayString(ctx.manifest, parsed.fieldKey, r, parsed.mode, ctx.locale);
2640
+ };
2641
+ var interpolateTemplateString = (template, ctx) => {
2642
+ let i = 0;
2643
+ let out = "";
2644
+ while (i < template.length) {
2645
+ const open = template.indexOf("{{", i);
2646
+ if (open === -1) {
2647
+ out += template.slice(i);
2648
+ break;
2649
+ }
2650
+ out += template.slice(i, open);
2651
+ const close = template.indexOf("}}", open + 2);
2652
+ if (close === -1) {
2653
+ out += template.slice(open);
2654
+ break;
2655
+ }
2656
+ const inner = template.slice(open + 2, close);
2657
+ const { exprPart, defaultValue } = parseDefaultFilter(inner);
2658
+ const parsed = parseExpression(exprPart);
2659
+ let value = evalParsed(parsed, ctx);
2660
+ if ((value === "" || value === void 0) && defaultValue !== void 0) {
2661
+ value = defaultValue;
2662
+ }
2663
+ out += value;
2664
+ i = close + 2;
2665
+ }
2666
+ return out;
2667
+ };
2668
+ var resolveAndInterpolateLocalizedText = (text, ctx) => {
2669
+ const localized = resolveLocalizedText(text, ctx.locale);
2670
+ return interpolateTemplateString(localized, ctx);
2671
+ };
2672
+ var localizedStrings = (t) => {
2673
+ const out = [t.default];
2674
+ if (t.translations) {
2675
+ for (const loc of Object.keys(t.translations)) {
2676
+ if (/^[a-z]{2}(-[A-Z]{2})?$/.test(loc)) {
2677
+ const v = t.translations[loc];
2678
+ if (v) out.push(v);
2679
+ }
2680
+ }
2681
+ }
2682
+ return out;
2683
+ };
2684
+ var collectOutgoingTargets = (screen) => {
2685
+ const out = [];
2686
+ if (screen.next.default) out.push(screen.next.default);
2687
+ walkScreen(screen, (l) => {
2688
+ if (l.kind === "single_choice" || l.kind === "multiple_choice") {
2689
+ if (l.branching.enabled) {
2690
+ for (const c of l.branching.conditions) out.push(c.goTo);
2691
+ }
2692
+ }
2693
+ if (l.kind === "button" && l.action.kind === "go_to_step") out.push(l.action.screenId);
2694
+ if (l.kind === "button" && l.action.kind === "request_os_permission") {
2695
+ const o = l.action.outcomes;
2696
+ for (const t of [o.granted, o.denied, o.blocked]) {
2697
+ if (t === OS_PERMISSION_OUTCOME_END) ; else if (t === OS_PERMISSION_OUTCOME_CONTINUE) {
2698
+ if (screen.next.default) out.push(screen.next.default);
2699
+ } else {
2700
+ out.push(t);
2701
+ }
2702
+ }
2703
+ }
2704
+ if (l.kind === "button" && l.action.kind === "go_back_one_screen" && l.action.fallbackScreenId) {
2705
+ out.push(l.action.fallbackScreenId);
2706
+ }
2707
+ if (l.kind === "back_button" && l.fallbackScreenId) out.push(l.fallbackScreenId);
2708
+ });
2709
+ return out;
2710
+ };
2711
+ var buildForwardAdjacency = (manifest) => {
2712
+ const m = /* @__PURE__ */ new Map();
2713
+ for (const s of manifest.screens) {
2714
+ m.set(s.id, collectOutgoingTargets(s));
2715
+ }
2716
+ for (const d of manifest.decisionNodes ?? []) {
2717
+ const outs = [
2718
+ ...d.cases.map((c) => c.next).filter((x) => x != null),
2719
+ d.elseNext
2720
+ ].filter((x) => x != null);
2721
+ m.set(d.id, outs);
2722
+ }
2723
+ return m;
2724
+ };
2725
+ var reverseAdjacency = (forward) => {
2726
+ const rev = /* @__PURE__ */ new Map();
2727
+ for (const [u, vs] of forward) {
2728
+ for (const v of vs) {
2729
+ const arr = rev.get(v) ?? [];
2730
+ arr.push(u);
2731
+ rev.set(v, arr);
2732
+ }
2733
+ }
2734
+ return rev;
2735
+ };
2736
+ var bfsForward = (entry, forward) => {
2737
+ if (entry == null) return /* @__PURE__ */ new Set();
2738
+ const seen = /* @__PURE__ */ new Set([entry]);
2739
+ const q = [entry];
2740
+ while (q.length) {
2741
+ const u = q.shift();
2742
+ for (const v of forward.get(u) ?? []) {
2743
+ if (!seen.has(v)) {
2744
+ seen.add(v);
2745
+ q.push(v);
2746
+ }
2747
+ }
2748
+ }
2749
+ return seen;
2750
+ };
2751
+ var reverseReachable = (targetId, rev, allowed) => {
2752
+ const seen = /* @__PURE__ */ new Set([targetId]);
2753
+ const q = [targetId];
2754
+ while (q.length) {
2755
+ const u = q.shift();
2756
+ for (const v of rev.get(u) ?? []) {
2757
+ if (!allowed.has(v)) continue;
2758
+ if (!seen.has(v)) {
2759
+ seen.add(v);
2760
+ q.push(v);
2761
+ }
2762
+ }
2763
+ }
2764
+ return seen;
2765
+ };
2766
+ var upstreamScreenIdsForPicker = (manifest, screenId) => {
2767
+ const forward = buildForwardAdjacency(manifest);
2768
+ const rev = reverseAdjacency(forward);
2769
+ const entry = manifest.entryScreenId;
2770
+ if (entry == null) return [];
2771
+ const reachFromEntry = bfsForward(entry, forward);
2772
+ const ancestors = reverseReachable(screenId, rev, reachFromEntry);
2773
+ const screenSet = new Set(manifest.screens.map((s) => s.id));
2774
+ return [...ancestors].filter((id) => id !== screenId && screenSet.has(id));
2775
+ };
2776
+ var computeDominators = (manifest) => {
2777
+ const forward = buildForwardAdjacency(manifest);
2778
+ const entry = manifest.entryScreenId;
2779
+ if (entry == null) return /* @__PURE__ */ new Map();
2780
+ const reachable = bfsForward(entry, forward);
2781
+ const nodes = [...reachable];
2782
+ const pred = /* @__PURE__ */ new Map();
2783
+ for (const [u, vs] of forward) {
2784
+ for (const v of vs) {
2785
+ if (!reachable.has(v)) continue;
2786
+ const arr = pred.get(v) ?? [];
2787
+ arr.push(u);
2788
+ pred.set(v, arr);
2789
+ }
2790
+ }
2791
+ const all = new Set(nodes);
2792
+ const dom = /* @__PURE__ */ new Map();
2793
+ for (const n of nodes) {
2794
+ if (n === entry) dom.set(n, /* @__PURE__ */ new Set([entry]));
2795
+ else dom.set(n, new Set(all));
2796
+ }
2797
+ let changed = true;
2798
+ while (changed) {
2799
+ changed = false;
2800
+ for (const n of nodes) {
2801
+ if (n === entry) continue;
2802
+ const ps = (pred.get(n) ?? []).filter((p) => reachable.has(p));
2803
+ let nextDom;
2804
+ if (ps.length === 0) {
2805
+ nextDom = /* @__PURE__ */ new Set([n]);
2806
+ } else {
2807
+ nextDom = /* @__PURE__ */ new Set([n]);
2808
+ const first = dom.get(ps[0]) ?? new Set(all);
2809
+ let inter = new Set(first);
2810
+ for (let i = 1; i < ps.length; i++) {
2811
+ const d = dom.get(ps[i]) ?? new Set(all);
2812
+ inter = new Set([...inter].filter((x) => d.has(x)));
2813
+ }
2814
+ for (const x of inter) nextDom.add(x);
2815
+ }
2816
+ const cur = dom.get(n);
2817
+ if (cur.size !== nextDom.size || [...nextDom].some((x) => !cur.has(x))) {
2818
+ dom.set(n, nextDom);
2819
+ changed = true;
2820
+ }
2821
+ }
2822
+ }
2823
+ return dom;
2824
+ };
2825
+ var fieldKeyOwnerScreenId = (manifest, fieldKey) => {
2826
+ for (const e of collectFieldKeys(manifest)) {
2827
+ if (e.fieldKey === fieldKey) return e.screenId;
2828
+ }
2829
+ return null;
2830
+ };
2831
+ var scanLocalizedForWarnings = (manifest, hostScreenId, text, layerId, dom, seen, out) => {
2832
+ const screenLabel = manifest.screens.find((s) => s.id === hostScreenId)?.name ?? hostScreenId;
2833
+ for (const str of localizedStrings(text)) {
2834
+ for (const inner of extractLiquidTemplateBodies(str)) {
2835
+ const { expr } = analyzeLiquidTemplateInner(inner);
2836
+ if (expr.kind === "invalid") {
2837
+ const key = `invalid:${hostScreenId}:${layerId}:${inner}`;
2838
+ if (seen.has(key)) continue;
2839
+ seen.add(key);
2840
+ out.push(
2841
+ `Warning: Screen "${screenLabel}" text layer "${layerId}" has an invalid template token "{{${inner}}}".`
2842
+ );
2843
+ continue;
2844
+ }
2845
+ if (expr.kind === "custom") {
2846
+ const key = `custom:${hostScreenId}`;
2847
+ if (!seen.has(key)) {
2848
+ seen.add(key);
2849
+ out.push(
2850
+ `Warning: Screen "${screenLabel}" uses \`custom.*\` template variables \u2014 values are supplied by the host SDK at runtime.`
2851
+ );
2852
+ }
2853
+ continue;
2854
+ }
2855
+ const ownerId = fieldKeyOwnerScreenId(manifest, expr.fieldKey);
2856
+ if (!ownerId) {
2857
+ const key = `unknown:${hostScreenId}:${layerId}:${expr.fieldKey}`;
2858
+ if (seen.has(key)) continue;
2859
+ seen.add(key);
2860
+ out.push(
2861
+ `Warning: Screen "${screenLabel}" references unknown variable "{{${expr.fieldKey}}}" (no matching input fieldKey).`
2862
+ );
2863
+ continue;
2864
+ }
2865
+ if (expr.mode === "id") {
2866
+ const ownerScreen = findScreen(manifest, ownerId);
2867
+ const input = ownerScreen ? findInputLayer(ownerScreen) : null;
2868
+ if (input?.kind !== "single_choice" && input?.kind !== "multiple_choice") {
2869
+ const key = `id:${hostScreenId}:${layerId}:${expr.fieldKey}`;
2870
+ if (seen.has(key)) continue;
2871
+ seen.add(key);
2872
+ out.push(
2873
+ `Warning: Screen "${screenLabel}" uses "{{${expr.fieldKey}.id}}" but "${expr.fieldKey}" is not a choice input \u2014 \`.id\` only applies to single or multiple choice.`
2874
+ );
2875
+ }
2876
+ }
2877
+ if (ownerId === hostScreenId) {
2878
+ const key = `same:${hostScreenId}:${layerId}:${expr.fieldKey}`;
2879
+ if (seen.has(key)) continue;
2880
+ seen.add(key);
2881
+ out.push(
2882
+ `Warning: Screen "${screenLabel}" references "{{${expr.fieldKey}}}" which is collected on the same screen \u2014 it will be empty until after the user submits.`
2883
+ );
2884
+ continue;
2885
+ }
2886
+ const dset = dom.get(hostScreenId);
2887
+ if (dset && !dset.has(ownerId)) {
2888
+ const key = `path:${hostScreenId}:${layerId}:${expr.fieldKey}`;
2889
+ if (seen.has(key)) continue;
2890
+ seen.add(key);
2891
+ out.push(
2892
+ `Warning: Screen "${screenLabel}" references "{{${expr.fieldKey}}}" which may be empty on some paths (not collected before this screen on every route).`
2893
+ );
2894
+ }
2895
+ }
2896
+ }
2897
+ };
2898
+ var collectInterpolationWarnings = (manifest) => {
2899
+ const dom = computeDominators(manifest);
2900
+ const out = [];
2901
+ const seen = /* @__PURE__ */ new Set();
2902
+ for (const screen of manifest.screens) {
2903
+ walkScreen(screen, (l) => {
2904
+ if (l.kind !== "text") return;
2905
+ scanLocalizedForWarnings(manifest, screen.id, l.text, l.id, dom, seen, out);
2906
+ });
2907
+ }
2908
+ return out;
2909
+ };
2910
+ var collectDecisionWarnings = (manifest) => {
2911
+ const dom = computeDominators(manifest);
2912
+ const out = [];
2913
+ for (const dn of manifest.decisionNodes ?? []) {
2914
+ const label = dn.name ?? dn.id;
2915
+ const dset = dom.get(dn.id);
2916
+ for (const fk of collectDecisionFieldKeysFromNode(dn)) {
2917
+ const ownerId = fieldKeyOwnerScreenId(manifest, fk);
2918
+ if (!ownerId) {
2919
+ out.push(`Warning: Decision "${label}" references unknown fieldKey "${fk}".`);
2920
+ continue;
2921
+ }
2922
+ if (dset && !dset.has(ownerId)) {
2923
+ out.push(
2924
+ `Warning: Decision "${label}" references "${fk}" which may be empty on some paths (not collected before this decision on every route).`
2925
+ );
2926
+ }
2927
+ }
2928
+ }
2929
+ return out;
2930
+ };
2931
+ var upstreamFieldKeysForPicker = (manifest, screenId) => {
2932
+ const upstream = new Set(upstreamScreenIdsForPicker(manifest, screenId));
2933
+ const keys = collectFieldKeys(manifest).filter((e) => upstream.has(e.screenId)).map((e) => e.fieldKey);
2934
+ return [...new Set(keys)].sort();
2935
+ };
2936
+
2937
+ // src/buildFlowPreview.ts
2938
+ var buildFlowPreview = (manifest) => {
2939
+ const screens = Array.isArray(manifest?.screens) ? manifest.screens : [];
2940
+ const stored = manifest?.builderMeta?.layout?.nodes ?? [];
2941
+ const positions = new Map(stored.map((n) => [n.id, { x: n.x, y: n.y }]));
2942
+ const nodes = screens.map((screen, idx) => {
2943
+ const pos = positions.get(screen.id) ?? { x: 80 + idx * 360, y: 120 };
2944
+ return {
2945
+ id: screen.id,
2946
+ x: pos.x,
2947
+ y: pos.y,
2948
+ isEntry: manifest?.entryScreenId === screen.id
2949
+ };
2950
+ });
2951
+ const edges = [];
2952
+ for (const screen of screens) {
2953
+ const input = findInputLayer(screen);
2954
+ const branchingOn = input && (input.kind === "single_choice" || input.kind === "multiple_choice") && input.branching.enabled;
2955
+ const nextDefault = screen.next?.default;
2956
+ if (branchingOn) {
2957
+ for (const cond of input.branching.conditions) {
2958
+ if (cond.goTo) {
2959
+ edges.push({ source: screen.id, target: cond.goTo, branching: true });
2960
+ }
2961
+ }
2962
+ if (nextDefault) {
2963
+ edges.push({ source: screen.id, target: nextDefault, branching: true });
2964
+ }
2965
+ } else if (nextDefault) {
2966
+ edges.push({ source: screen.id, target: nextDefault });
2967
+ }
2968
+ }
2969
+ return { nodes, edges };
2970
+ };
2971
+
2972
+ // src/counterLayer.ts
2973
+ var formatCounterLayerValue = (n, decimalPlaces = 0) => {
2974
+ if (!Number.isFinite(n)) return "";
2975
+ const p = Math.max(0, Math.min(20, Math.trunc(decimalPlaces)));
2976
+ const s = n.toFixed(p);
2977
+ if (p === 0) return s;
2978
+ const trimmed = s.replace(/\.?0+$/, "");
2979
+ return trimmed === "" || trimmed === "-" ? "0" : trimmed;
2980
+ };
2981
+ var pad2 = (n) => String(n).padStart(2, "0");
2982
+ var formatCounterAsTime = (totalSeconds, format) => {
2983
+ if (!Number.isFinite(totalSeconds)) return "";
2984
+ const whole = Math.max(0, Math.floor(totalSeconds));
2985
+ switch (format) {
2986
+ case "mm_ss": {
2987
+ const minutes = Math.floor(whole / 60);
2988
+ const seconds = whole % 60;
2989
+ return `${minutes}:${pad2(seconds)}`;
2990
+ }
2991
+ case "hh_mm_ss": {
2992
+ const hours = Math.floor(whole / 3600);
2993
+ const minutes = Math.floor(whole % 3600 / 60);
2994
+ const seconds = whole % 60;
2995
+ return `${hours}:${pad2(minutes)}:${pad2(seconds)}`;
2996
+ }
2997
+ case "dd_hh_mm_ss": {
2998
+ const days = Math.floor(whole / 86400);
2999
+ const hours = Math.floor(whole % 86400 / 3600);
3000
+ const minutes = Math.floor(whole % 3600 / 60);
3001
+ const seconds = whole % 60;
3002
+ return `${days}:${pad2(hours)}:${pad2(minutes)}:${pad2(seconds)}`;
3003
+ }
3004
+ default:
3005
+ return "";
3006
+ }
3007
+ };
3008
+ var formatCounterLayerDisplay = (n, opts) => {
3009
+ const kind = opts.displayKind ?? "number";
3010
+ if (kind === "time") {
3011
+ const tf = opts.timeFormat ?? "mm_ss";
3012
+ return formatCounterAsTime(n, tf);
3013
+ }
3014
+ return formatCounterLayerValue(n, opts.decimalPlaces ?? 0);
3015
+ };
3016
+ var splitSecondsForTimeFormat = (totalSeconds, format) => {
3017
+ const whole = Math.max(0, Math.floor(Number.isFinite(totalSeconds) ? totalSeconds : 0));
3018
+ switch (format) {
3019
+ case "mm_ss":
3020
+ return {
3021
+ days: 0,
3022
+ hours: 0,
3023
+ minutes: Math.floor(whole / 60),
3024
+ seconds: whole % 60
3025
+ };
3026
+ case "hh_mm_ss": {
3027
+ const hours = Math.floor(whole / 3600);
3028
+ const minutes = Math.floor(whole % 3600 / 60);
3029
+ const seconds = whole % 60;
3030
+ return { days: 0, hours, minutes, seconds };
3031
+ }
3032
+ case "dd_hh_mm_ss": {
3033
+ const days = Math.floor(whole / 86400);
3034
+ const hours = Math.floor(whole % 86400 / 3600);
3035
+ const minutes = Math.floor(whole % 3600 / 60);
3036
+ const seconds = whole % 60;
3037
+ return { days, hours, minutes, seconds };
3038
+ }
3039
+ default:
3040
+ return { days: 0, hours: 0, minutes: 0, seconds: 0 };
3041
+ }
3042
+ };
3043
+ var clamp59 = (n) => Math.max(0, Math.min(59, Math.trunc(Number.isFinite(n) ? n : 0)));
3044
+ var clampHour23 = (n) => Math.max(0, Math.min(23, Math.trunc(Number.isFinite(n) ? n : 0)));
3045
+ var mergePartsForTimeFormat = (parts, format) => {
3046
+ switch (format) {
3047
+ case "mm_ss":
3048
+ return Math.max(0, Math.trunc(Number.isFinite(parts.minutes) ? parts.minutes : 0)) * 60 + clamp59(parts.seconds);
3049
+ case "hh_mm_ss":
3050
+ return Math.max(0, Math.trunc(Number.isFinite(parts.hours) ? parts.hours : 0)) * 3600 + clamp59(parts.minutes) * 60 + clamp59(parts.seconds);
3051
+ case "dd_hh_mm_ss":
3052
+ return Math.max(0, Math.trunc(Number.isFinite(parts.days) ? parts.days : 0)) * 86400 + clampHour23(parts.hours) * 3600 + clamp59(parts.minutes) * 60 + clamp59(parts.seconds);
3053
+ default:
3054
+ return 0;
3055
+ }
3056
+ };
3057
+ var resolveCounterAnimationDurationMs = (opts) => {
3058
+ if ((opts.displayKind ?? "number") === "time") {
3059
+ return Math.max(0, Math.round(Math.abs(opts.endValue - opts.startValue) * 1e3));
3060
+ }
3061
+ return opts.durationMs ?? 3e3;
3062
+ };
3063
+
3064
+ // src/translationPlaceholders.ts
3065
+ var sortedJson = (tokens) => JSON.stringify([...tokens].sort());
3066
+ var stripLiquidRegions = (s) => {
3067
+ let out = "";
3068
+ let i = 0;
3069
+ while (i < s.length) {
3070
+ const open = s.indexOf("{{", i);
3071
+ if (open === -1) return out + s.slice(i);
3072
+ out += s.slice(i, open);
3073
+ const close = s.indexOf("}}", open + 2);
3074
+ if (close === -1) return out + s.slice(open);
3075
+ i = close + 2;
3076
+ }
3077
+ return out;
3078
+ };
3079
+ var extractTranslationPlaceholders = (s) => {
3080
+ const liquids = extractLiquidTemplateBodies(s).map((inner) => `{{${inner.trim()}}}`);
3081
+ const stripped = stripLiquidRegions(s);
3082
+ const singles = [];
3083
+ const re = /\{([a-zA-Z_]\w*)\}/g;
3084
+ for (const m of stripped.matchAll(re)) {
3085
+ singles.push(`{${m[1]}}`);
3086
+ }
3087
+ return [...liquids, ...singles];
3088
+ };
3089
+ var translationPlaceholdersMatch = (source, translated) => sortedJson(extractTranslationPlaceholders(source)) === sortedJson(extractTranslationPlaceholders(translated));
3090
+
3091
+ // src/aiFlowGenerationMerge.ts
3092
+ var AI_FLOW_PLACEHOLDER_SCREEN_ID = "scr_blank";
3093
+ var bodyStackChildrenLen = (screen) => {
3094
+ const body = screen.regions.body;
3095
+ if (body.kind !== "stack") return -1;
3096
+ return body.children.length;
3097
+ };
3098
+ var isAiFlowPlaceholderManifest = (manifest) => manifest.screens.length === 1 && manifest.screens[0]?.id === AI_FLOW_PLACEHOLDER_SCREEN_ID && bodyStackChildrenLen(manifest.screens[0]) === 0;
3099
+ var findDefaultPathTailScreen = (manifest) => {
3100
+ const map = new Map(manifest.screens.map((s) => [s.id, s]));
3101
+ const startId = manifest.entryScreenId ?? manifest.screens[0]?.id;
3102
+ let cur = startId ? map.get(startId) : void 0;
3103
+ const seen = /* @__PURE__ */ new Set();
3104
+ let last;
3105
+ while (cur && !seen.has(cur.id)) {
3106
+ seen.add(cur.id);
3107
+ last = cur;
3108
+ const n = cur.next.default;
3109
+ if (!n) break;
3110
+ cur = map.get(n);
3111
+ }
3112
+ return last;
3113
+ };
3114
+ var ensureLayoutNodes = (manifest) => {
3115
+ const existing = new Map(
3116
+ (manifest.builderMeta?.layout?.nodes ?? []).map((n) => [n.id, n])
3117
+ );
3118
+ const nodes = manifest.screens.map((s, idx) => {
3119
+ const hit = existing.get(s.id);
3120
+ if (hit) return { ...hit };
3121
+ return { id: s.id, x: 80 + idx * 360, y: 120 };
3122
+ });
3123
+ return {
3124
+ ...manifest,
3125
+ builderMeta: {
3126
+ ...manifest.builderMeta ?? {},
3127
+ layout: {
3128
+ ...manifest.builderMeta?.layout ?? {},
3129
+ nodes
3130
+ }
3131
+ }
3132
+ };
3133
+ };
3134
+ var mergeAiGeneratedScreenIntoManifest = (manifest, screen) => {
3135
+ if (isAiFlowPlaceholderManifest(manifest)) {
3136
+ const screens2 = [{ ...screen, next: { default: null } }];
3137
+ let builderMeta = manifest.builderMeta;
3138
+ const oldEntry = manifest.entryScreenId;
3139
+ if (builderMeta?.layout?.nodes?.length) {
3140
+ builderMeta = {
3141
+ ...builderMeta,
3142
+ layout: {
3143
+ ...builderMeta.layout,
3144
+ nodes: builderMeta.layout.nodes.map(
3145
+ (n) => n.id === oldEntry ? { ...n, id: screen.id } : { ...n }
3146
+ )
3147
+ }
3148
+ };
3149
+ }
3150
+ const merged2 = {
3151
+ ...manifest,
3152
+ screens: screens2,
3153
+ entryScreenId: screen.id,
3154
+ builderMeta
3155
+ };
3156
+ return ensureLayoutNodes(merged2);
3157
+ }
3158
+ const tail = findDefaultPathTailScreen(manifest);
3159
+ if (!tail) {
3160
+ throw new Error("mergeAiGeneratedScreenIntoManifest: no tail on default path");
3161
+ }
3162
+ if (tail.next.default !== null) {
3163
+ throw new Error("mergeAiGeneratedScreenIntoManifest: tail must end the default path (next is null)");
3164
+ }
3165
+ const screens = manifest.screens.map(
3166
+ (s) => s.id === tail.id ? { ...s, next: { default: screen.id } } : s
3167
+ );
3168
+ screens.push({ ...screen, next: { default: null } });
3169
+ const merged = { ...manifest, screens };
3170
+ return ensureLayoutNodes(merged);
3171
+ };
3172
+ var appendGeneratedScreenToManifest = (manifest, newScreen) => {
3173
+ const wiredNew = {
3174
+ ...newScreen,
3175
+ next: { default: null }
3176
+ };
3177
+ const merged = {
3178
+ ...manifest,
3179
+ screens: [...manifest.screens, wiredNew]
3180
+ };
3181
+ return ensureLayoutNodes(merged);
3182
+ };
3183
+ var insertScreenAfterAnchorInManifest = (manifest, anchorScreenId, newScreen) => {
3184
+ const anchor = manifest.screens.find((s) => s.id === anchorScreenId);
3185
+ if (!anchor) {
3186
+ throw new Error("insertScreenAfterAnchorInManifest: anchor screen not found");
3187
+ }
3188
+ const forwarded = anchor.next.default;
3189
+ const wiredNew = {
3190
+ ...newScreen,
3191
+ next: { default: forwarded ?? null }
3192
+ };
3193
+ const screens = manifest.screens.map(
3194
+ (s) => s.id === anchorScreenId ? { ...s, next: { default: wiredNew.id } } : s
3195
+ );
3196
+ screens.push(wiredNew);
3197
+ const merged = { ...manifest, screens };
3198
+ return ensureLayoutNodes(merged);
3199
+ };
3200
+ var mergeSlimManifestPreservingBuilderMeta = (draft, slim) => {
3201
+ const merged = {
3202
+ ...slim,
3203
+ builderMeta: draft.builderMeta
3204
+ };
3205
+ return ensureLayoutNodes(merged);
3206
+ };
3207
+
3208
+ // src/rheoAgentManifestMerge.ts
3209
+ var upsertById = (existing, incoming) => {
3210
+ const incomingById = new Map(incoming.map((item) => [item.id, item]));
3211
+ const merged = existing.map((item) => incomingById.get(item.id) ?? item);
3212
+ const existingIds = new Set(existing.map((item) => item.id));
3213
+ for (const item of incoming) {
3214
+ if (!existingIds.has(item.id)) merged.push(item);
3215
+ }
3216
+ return merged;
3217
+ };
3218
+ var mergeRheoAgentPatchIntoDraft = (draft, patch) => {
3219
+ const removeIds = new Set(patch.removeScreenIds ?? []);
3220
+ let screens = patch.screens ? upsertById(draft.screens, patch.screens) : draft.screens;
3221
+ if (removeIds.size > 0) {
3222
+ screens = screens.filter((screen) => !removeIds.has(screen.id));
3223
+ }
3224
+ const decisionNodes = patch.decisionNodes ? upsertById(draft.decisionNodes ?? [], patch.decisionNodes) : draft.decisionNodes;
3225
+ const externalSurfaceNodes = patch.externalSurfaceNodes ? upsertById(draft.externalSurfaceNodes ?? [], patch.externalSurfaceNodes) : draft.externalSurfaceNodes;
3226
+ const merged = {
3227
+ ...draft,
3228
+ screens,
3229
+ decisionNodes,
3230
+ externalSurfaceNodes,
3231
+ ...patch.entryScreenId !== void 0 ? { entryScreenId: patch.entryScreenId } : {},
3232
+ ...patch.sdkAttributeKeys !== void 0 ? { sdkAttributeKeys: patch.sdkAttributeKeys } : {},
3233
+ builderMeta: draft.builderMeta
3234
+ };
3235
+ return ensureLayoutNodes(merged);
3236
+ };
3237
+ var randomHex = (byteLength) => {
3238
+ const bytes = new Uint8Array(byteLength);
3239
+ if (typeof globalThis.crypto?.getRandomValues === "function") {
3240
+ globalThis.crypto.getRandomValues(bytes);
3241
+ } else {
3242
+ for (let i = 0; i < byteLength; i++) bytes[i] = Math.floor(Math.random() * 256);
3243
+ }
3244
+ return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
3245
+ };
3246
+ var collectManifestLayerIds = (manifest) => {
3247
+ const ids = /* @__PURE__ */ new Set();
3248
+ for (const s of manifest.screens) {
3249
+ walkScreenLayers(s, (l) => {
3250
+ ids.add(l.id);
3251
+ });
3252
+ }
3253
+ return ids;
3254
+ };
3255
+ var remapScreenIdsForManifestIngest = (manifest, screen) => {
3256
+ const screenIds = new Set(manifest.screens.map((s) => s.id));
3257
+ let newScreenId = `scr_ai_${randomHex(4)}`;
3258
+ let sn = 0;
3259
+ while (screenIds.has(newScreenId)) {
3260
+ newScreenId = `scr_ai_${randomHex(4)}_${sn++}`.slice(0, 64);
3261
+ }
3262
+ const usedLayers = collectManifestLayerIds(manifest);
3263
+ const token = randomHex(3);
3264
+ const map = /* @__PURE__ */ new Map();
3265
+ walkScreenLayers(screen, (l) => {
3266
+ if (map.has(l.id)) return;
3267
+ const tail = l.id.replace(/^lyr_/i, "").replace(/[^a-z0-9_]/gi, "_") || "layer";
3268
+ let nid = `lyr_ai_${token}_${tail}`;
3269
+ let i = 0;
3270
+ for (; ; ) {
3271
+ const taken = usedLayers.has(nid) || [...map.values()].some((v) => v === nid);
3272
+ if (!taken) break;
3273
+ i += 1;
3274
+ nid = `lyr_ai_${token}_${tail}_${i}`.slice(0, 64);
3275
+ }
3276
+ map.set(l.id, nid);
3277
+ usedLayers.add(nid);
3278
+ });
3279
+ let json = JSON.stringify({ ...screen, id: newScreenId });
3280
+ const pairs = [...map.entries()].sort((a, b) => b[0].length - a[0].length);
3281
+ for (const [oldId, newId] of pairs) {
3282
+ json = json.split(oldId).join(newId);
3283
+ }
3284
+ return JSON.parse(json);
3285
+ };
3286
+ var remapReplacedScreenLayerCollisionsInManifest = (manifest, targetScreenId, screen) => {
3287
+ const forbidden = /* @__PURE__ */ new Set();
3288
+ for (const s of manifest.screens) {
3289
+ if (s.id === targetScreenId) continue;
3290
+ walkScreenLayers(s, (l) => forbidden.add(l.id));
3291
+ }
3292
+ const next = JSON.parse(JSON.stringify(screen));
3293
+ const seen = /* @__PURE__ */ new Set();
3294
+ const used = new Set(forbidden);
3295
+ const alloc = () => {
3296
+ let i = 0;
3297
+ for (; ; ) {
3298
+ const nid = `lyr_ai_${randomHex(4)}_${i++}`.slice(0, 64);
3299
+ if (!used.has(nid)) {
3300
+ used.add(nid);
3301
+ return nid;
3302
+ }
3303
+ }
3304
+ };
3305
+ const fix = (layer) => {
3306
+ if (forbidden.has(layer.id) || seen.has(layer.id)) {
3307
+ layer.id = alloc();
3308
+ }
3309
+ seen.add(layer.id);
3310
+ if (layer.kind === "stack") layer.children.forEach(fix);
3311
+ else if (layer.kind === "carousel") layer.slides.forEach(fix);
3312
+ else if (layer.kind === "button" || layer.kind === "back_button") layer.children.forEach(fix);
3313
+ else if (layer.kind === "hyperlink") layer.children.forEach(fix);
3314
+ else if (layer.kind === "single_choice" || layer.kind === "multiple_choice") {
3315
+ layer.children.forEach(fix);
3316
+ } else if (layer.kind === "text_input" || layer.kind === "scale_input") {
3317
+ layer.children?.forEach(fix);
3318
+ } else if (layer.kind === "oauth_login") layer.children.forEach(fix);
3319
+ else if (layer.kind === "oauth_provider" && layer.variant === "custom") layer.children.forEach(fix);
3320
+ else if (layer.kind === "email_password_auth") layer.children.forEach(fix);
3321
+ else if (layer.kind === "email_password_field") layer.children?.forEach(fix);
3322
+ else if (layer.kind === "email_password_submit") layer.children.forEach(fix);
3323
+ };
3324
+ if (next.regions.header) fix(next.regions.header);
3325
+ fix(next.regions.body);
3326
+ if (next.regions.footer) fix(next.regions.footer);
3327
+ return next;
3328
+ };
3329
+ var FG_LIGHT = "#0a0a0a";
3330
+ var FG_DARK = "#fafafa";
3331
+ var buttonVariantChromeForTheme = (variant, theme) => {
3332
+ const dark = theme === "dark";
3333
+ switch (variant) {
3334
+ case "primary":
3335
+ return dark ? { bg: FG_DARK, color: FG_LIGHT, border: "transparent" } : { bg: FG_LIGHT, color: FG_DARK, border: "transparent" };
3336
+ case "secondary":
3337
+ return dark ? { bg: "transparent", color: FG_DARK, border: "#27272a" } : { bg: "transparent", color: FG_LIGHT, border: "#e4e4e7" };
3338
+ case "ghost":
3339
+ return dark ? { bg: "transparent", color: FG_DARK, border: "transparent" } : { bg: "transparent", color: FG_LIGHT, border: "transparent" };
3340
+ case "destructive":
3341
+ return dark ? { bg: "#ef4444", color: FG_DARK, border: "transparent" } : { bg: "#dc2626", color: FG_DARK, border: "transparent" };
3342
+ }
3343
+ };
3344
+ var pairThemedColor = (light, dark) => light === dark ? light : { light, dark };
3345
+ var buttonVariantAuthoringStyleDefaults = (variant) => {
3346
+ const l = buttonVariantChromeForTheme(variant, "light");
3347
+ const d = buttonVariantChromeForTheme(variant, "dark");
3348
+ return {
3349
+ /** Matches web sim `buttonBaseStyle` (radius, type scale, width). */
3350
+ radius: 10,
3351
+ fontSize: 13,
3352
+ fontWeight: 600,
3353
+ width: "full",
3354
+ height: "auto",
3355
+ background: pairThemedColor(l.bg, d.bg),
3356
+ color: pairThemedColor(l.color, d.color),
3357
+ border: {
3358
+ width: 1,
3359
+ color: pairThemedColor(l.border, d.border)
3360
+ }
3361
+ };
3362
+ };
3363
+ var mergeAuthoringButtonStyleWithLayer = (variant, style) => deepMergeStyle(
3364
+ buttonVariantAuthoringStyleDefaults(variant),
3365
+ style ?? {}
3366
+ );
3367
+ var buttonPaletteBorderFallback = (variant, theme, authoredStyle, resolvedBackground) => {
3368
+ const authoredWidth = authoredStyle?.border?.width;
3369
+ if (authoredWidth !== void 0) {
3370
+ return { borderWidth: authoredWidth, borderColor: void 0 };
3371
+ }
3372
+ if (resolvedBackground === "transparent") {
3373
+ return { borderWidth: 0, borderColor: void 0 };
3374
+ }
3375
+ const chrome = buttonVariantChromeForTheme(variant, theme);
3376
+ return {
3377
+ borderWidth: chrome.border === "transparent" ? 0 : 1,
3378
+ borderColor: chrome.border === "transparent" ? void 0 : chrome.border
3379
+ };
3380
+ };
3381
+
3382
+ // src/manifestBillingSlice.ts
3383
+ var cloneManifestForBillingSlice = (m) => structuredClone(m);
3384
+ var stripRestingMotionFromLayer = (layer) => {
3385
+ const l = layer;
3386
+ delete l.restingMotion;
3387
+ delete l.restingMotions;
3388
+ };
3389
+ var stripManifestAnimations = (manifest) => {
3390
+ const next = cloneManifestForBillingSlice(manifest);
3391
+ for (const screen of next.screens) {
3392
+ const s = screen;
3393
+ delete s.animations;
3394
+ delete s.stagger;
3395
+ }
3396
+ return next;
3397
+ };
3398
+ var stripManifestMotion = (manifest) => {
3399
+ const next = stripManifestAnimations(manifest);
3400
+ for (const screen of next.screens) {
3401
+ walkScreen(screen, stripRestingMotionFromLayer);
3402
+ }
3403
+ return next;
3404
+ };
3405
+ var collapseLocalizedObjects = (node) => {
3406
+ if (node === null || node === void 0) return;
3407
+ if (Array.isArray(node)) {
3408
+ for (const x of node) collapseLocalizedObjects(x);
3409
+ return;
3410
+ }
3411
+ if (typeof node !== "object") return;
3412
+ const o = node;
3413
+ if (typeof o.default === "string" && Object.prototype.hasOwnProperty.call(o, "translations")) {
3414
+ delete o.translations;
3415
+ }
3416
+ for (const v of Object.values(o)) collapseLocalizedObjects(v);
3417
+ };
3418
+ var collapseManifestToDefaultLocaleOnly = (manifest) => {
3419
+ const next = cloneManifestForBillingSlice(manifest);
3420
+ collapseLocalizedObjects(next);
3421
+ next.locales = [next.defaultLocale];
3422
+ return next;
3423
+ };
3424
+ var manifestHasScreenAnimations = (manifest) => manifest.screens.some((s) => {
3425
+ const anim = s.animations;
3426
+ return Array.isArray(anim) && anim.length > 0;
3427
+ });
3428
+ var recordHasNonEmptyTranslations = (node) => {
3429
+ if (node === null || node === void 0) return false;
3430
+ if (Array.isArray(node)) return node.some(recordHasNonEmptyTranslations);
3431
+ if (typeof node !== "object") return false;
3432
+ const o = node;
3433
+ const tr = o.translations;
3434
+ if (tr && typeof tr === "object" && Object.keys(tr).length > 0) return true;
3435
+ return Object.values(o).some(recordHasNonEmptyTranslations);
3436
+ };
3437
+ var manifestUsesTranslationsBeyondDefault = (manifest) => {
3438
+ if (manifest.locales.length > 1) return true;
3439
+ return recordHasNonEmptyTranslations(manifest);
3440
+ };
3441
+ var manifestUsesExternalIntegrations = (manifest) => (manifest.externalSurfaceNodes ?? []).some(
3442
+ (n) => n.config?.provider != null && n.config.provider !== "unspecified"
3443
+ );
3444
+ var resolveHyperlinkPreviewLabel = (roots, args) => {
3445
+ const { manifest, locale, interpolationContext } = args;
3446
+ let found = "";
3447
+ const visit = (l) => {
3448
+ if (found) return;
3449
+ if (l.kind === "text") {
3450
+ found = interpolationContext ? resolveAndInterpolateLocalizedText(l.text, {
3451
+ manifest,
3452
+ locale,
3453
+ responses: interpolationContext.responses,
3454
+ customProperties: interpolationContext.customProperties
3455
+ }) : resolveLocalizedText(l.text, locale);
3456
+ return;
3457
+ }
3458
+ if (l.kind === "stack") {
3459
+ for (const c of l.children) visit(c);
3460
+ return;
3461
+ }
3462
+ if (l.kind === "carousel") {
3463
+ for (const s of l.slides) visit(s);
3464
+ return;
3465
+ }
3466
+ if (l.kind === "button" || l.kind === "back_button") {
3467
+ l.children.forEach(visit);
3468
+ return;
3469
+ }
3470
+ if (l.kind === "hyperlink") {
3471
+ l.children.forEach(visit);
3472
+ return;
3473
+ }
3474
+ if (l.kind === "single_choice" || l.kind === "multiple_choice") {
3475
+ l.children.forEach(visit);
3476
+ return;
3477
+ }
3478
+ if (l.kind === "text_input" || l.kind === "scale_input") {
3479
+ l.children?.forEach(visit);
3480
+ return;
3481
+ }
3482
+ if (l.kind === "oauth_login") {
3483
+ l.children.forEach(visit);
3484
+ return;
3485
+ }
3486
+ if (l.kind === "oauth_provider" && l.variant === "custom") {
3487
+ l.children.forEach(visit);
3488
+ return;
3489
+ }
3490
+ if (l.kind === "email_password_auth") {
3491
+ l.children.forEach(visit);
3492
+ return;
3493
+ }
3494
+ if (l.kind === "email_password_field") {
3495
+ l.children?.forEach(visit);
3496
+ return;
3497
+ }
3498
+ if (l.kind === "email_password_submit") {
3499
+ l.children.forEach(visit);
3500
+ return;
3501
+ }
3502
+ };
3503
+ for (const r of roots) visit(r);
3504
+ return found;
3505
+ };
3506
+
3507
+ // src/layerTypography.ts
3508
+ var TEXT_FONT_FAMILY_SYSTEM_UI = "__rheo_system_ui__";
3509
+ var WEB_DOCUMENT_FONT_STACK = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
3510
+ var quoteCssFamilyName = (name) => {
3511
+ const t = name.trim();
3512
+ if (!t) return "";
3513
+ const safe = /^[-_a-zA-Z0-9]+$/.test(t);
3514
+ return safe ? t : JSON.stringify(t);
3515
+ };
3516
+ var fontStackWithPrimary = (primary) => primary ? `${primary}, ${WEB_DOCUMENT_FONT_STACK}` : WEB_DOCUMENT_FONT_STACK;
3517
+ var resolveWebRootFontFamilyCss = (theme) => {
3518
+ const raw = theme?.fontFamily?.trim();
3519
+ if (!raw) return void 0;
3520
+ if (raw.includes(",")) return raw;
3521
+ return fontStackWithPrimary(quoteCssFamilyName(raw));
3522
+ };
3523
+ var resolveWebTextFontFamilyCss = (layerFont) => {
3524
+ if (layerFont === TEXT_FONT_FAMILY_SYSTEM_UI) return WEB_DOCUMENT_FONT_STACK;
3525
+ const raw = layerFont?.trim();
3526
+ if (!raw) return void 0;
3527
+ if (raw.includes(",")) return raw;
3528
+ return fontStackWithPrimary(quoteCssFamilyName(raw));
3529
+ };
3530
+ var brandingWebFontFacesCss = (branding) => {
3531
+ if (!branding?.fontFamilies?.length) return "";
3532
+ const blocks = [];
3533
+ for (const fam of branding.fontFamilies) {
3534
+ const famCss = quoteCssFamilyName(fam.name);
3535
+ for (const st of fam.styles) {
3536
+ if (!st.url?.trim()) continue;
3537
+ const url = JSON.stringify(st.url);
3538
+ blocks.push(
3539
+ `@font-face{font-family:${famCss};src:url(${url});font-weight:${st.weight};font-style:${st.italic ? "italic" : "normal"};font-display:swap;}`
3540
+ );
3541
+ }
3542
+ }
3543
+ return blocks.join("");
3544
+ };
3545
+ var nativeFontRegistrationNameForStyle = (styleId) => `RheoFont__${styleId}`;
3546
+ var buildBrandingFontLoadMap = (branding) => {
3547
+ const map = {};
3548
+ if (!branding?.fontFamilies?.length) return map;
3549
+ for (const fam of branding.fontFamilies) {
3550
+ for (const st of fam.styles) {
3551
+ const u = st.url?.trim();
3552
+ if (!u) continue;
3553
+ map[nativeFontRegistrationNameForStyle(st.id)] = u;
3554
+ }
3555
+ }
3556
+ return map;
3557
+ };
3558
+ var resolveNativeTextFontFamilyName = (branding, logicalFamily, fontWeight) => {
3559
+ if (!logicalFamily?.trim()) return void 0;
3560
+ if (logicalFamily === TEXT_FONT_FAMILY_SYSTEM_UI) return void 0;
3561
+ const fam = branding?.fontFamilies.find((f) => f.name === logicalFamily);
3562
+ if (!fam) return logicalFamily;
3563
+ const w = fontWeight ?? 400;
3564
+ const withUrl = fam.styles.filter((s) => !!s.url?.trim());
3565
+ if (withUrl.length === 0) return logicalFamily;
3566
+ const exact = withUrl.find((s) => s.weight === w && !s.italic);
3567
+ const st = exact ?? withUrl.find((s) => s.weight === w) ?? withUrl[0];
3568
+ if (!st) return logicalFamily;
3569
+ return nativeFontRegistrationNameForStyle(st.id);
3570
+ };
3571
+
3572
+ // src/layerRotate.ts
3573
+ var LAYER_ROTATE_DEG_MIN = -360;
3574
+ var LAYER_ROTATE_DEG_MAX = 360;
3575
+ var layerRotateCssTransform = (deg) => deg === void 0 ? void 0 : `rotate(${deg}deg)`;
3576
+ var layerRotateNativeTransform = (deg) => deg === void 0 ? void 0 : [{ rotate: `${deg}deg` }];
3577
+
3578
+ // src/checkboxGlyphStyle.ts
3579
+ var DEFAULT_UNCHECKED = {
3580
+ size: 18,
3581
+ radius: 4,
3582
+ border: { width: 2, color: { light: "#71717a", dark: "#a1a1aa" } },
3583
+ background: "transparent",
3584
+ opacity: 1
3585
+ };
3586
+ var DEFAULT_CHECKED_OVERLAY = {
3587
+ background: { light: "#18181b", dark: "#e4e4e7" },
3588
+ checkColor: { light: "#ffffff", dark: "#18181b" }
3589
+ };
3590
+ var mergeBorder = (a, b) => {
3591
+ if (!a && !b) return void 0;
3592
+ return { ...a, ...b };
3593
+ };
3594
+ var mergeGlyph = (a, b) => {
3595
+ if (!b) return { ...a };
3596
+ const out = { ...a };
3597
+ Object.keys(b).forEach((k) => {
3598
+ const v = b[k];
3599
+ if (v === void 0) return;
3600
+ if (k === "border") {
3601
+ const merged = mergeBorder(out.border, v);
3602
+ if (merged && Object.values(merged).some((x) => x !== void 0)) out.border = merged;
3603
+ else delete out.border;
3604
+ return;
3605
+ }
3606
+ if (k === "shadow") {
3607
+ out.shadow = v;
3608
+ return;
3609
+ }
3610
+ out[k] = v;
3611
+ });
3612
+ return out;
3613
+ };
3614
+ var mergeCheckboxGlyphStyle = (prev, patch) => {
3615
+ const out = { ...prev ?? {} };
3616
+ for (const key of Object.keys(patch)) {
3617
+ const v = patch[key];
3618
+ if (key === "border") {
3619
+ if (v === void 0) delete out.border;
3620
+ else out.border = mergeBorder(out.border, v);
3621
+ if (out.border && !Object.values(out.border).some((x) => x !== void 0)) delete out.border;
3622
+ continue;
3623
+ }
3624
+ if (key === "shadow") {
3625
+ if (v === void 0) delete out.shadow;
3626
+ else out.shadow = v;
3627
+ continue;
3628
+ }
3629
+ if (v === void 0) delete out[key];
3630
+ else out[key] = v;
3631
+ }
3632
+ return Object.keys(out).length > 0 ? out : void 0;
3633
+ };
3634
+ var resolveCheckboxGlyphForRender = (theme, palette, unchecked, checkedOverlay, isChecked, branding) => {
3635
+ const base = mergeGlyph(DEFAULT_UNCHECKED, unchecked);
3636
+ const merged = isChecked ? mergeGlyph(mergeGlyph(base, DEFAULT_CHECKED_OVERLAY), checkedOverlay) : base;
3637
+ const sizePx = merged.size ?? 18;
3638
+ const radiusPx = merged.radius ?? 4;
3639
+ const borderWidth = merged.border?.width;
3640
+ const borderColor = resolveThemedColor(theme, palette, merged.border?.color);
3641
+ const background = resolveThemedBackground(theme, branding, palette, merged.background);
3642
+ const nb = nativeBrandBackgroundFromThemedColor(theme, branding, palette, merged.background);
3643
+ const opacity = merged.opacity ?? 1;
3644
+ const checkColor = isChecked ? resolveThemedColor(theme, palette, merged.checkColor) : void 0;
3645
+ return {
3646
+ sizePx,
3647
+ radiusPx,
3648
+ background,
3649
+ nativeBackgroundColor: nb.solid,
3650
+ nativeLinearGradient: nb.linear ?? null,
3651
+ borderWidth,
3652
+ borderColor,
3653
+ opacity,
3654
+ checkColor,
3655
+ shadow: merged.shadow
3656
+ };
3657
+ };
3658
+
3659
+ // src/scaleInputStyle.ts
3660
+ var defaultTrackColor = (palette) => palette === "dark" ? "#3f3f46" : "#e4e4e7";
3661
+ var defaultFillColor = (theme, palette) => {
3662
+ const fromTheme = theme?.primary ? resolveThemedColor(theme, palette, theme.primary) : void 0;
3663
+ return fromTheme ?? (palette === "dark" ? "#fafafa" : "#0a0a0a");
3664
+ };
3665
+ var defaultLabelColor = (palette) => palette === "dark" ? "#a1a1aa" : "#52525b";
3666
+ var defaultValueColor = (palette) => palette === "dark" ? "#fafafa" : "#0a0a0a";
3667
+ var mergeScaleInputLabelStyle = (prev, patch) => {
3668
+ const out = { ...prev ?? {} };
3669
+ for (const key of Object.keys(patch)) {
3670
+ const v = patch[key];
3671
+ if (v === void 0) delete out[key];
3672
+ else out[key] = v;
3673
+ }
3674
+ return Object.keys(out).length > 0 ? out : void 0;
3675
+ };
3676
+ var mergeScaleInputValueStyle = mergeScaleInputLabelStyle;
3677
+ var resolveColor = (theme, palette, color, fallback) => resolveThemedColor(theme, palette, color) ?? fallback;
3678
+ var resolveTextStyle = (style, theme, palette, defaults) => ({
3679
+ fontFamily: style?.fontFamily,
3680
+ fontSizePx: style?.fontSize ?? defaults.fontSizePx,
3681
+ fontWeight: style?.fontWeight ?? defaults.fontWeight,
3682
+ color: resolveColor(theme, palette, style?.color, defaults.color),
3683
+ textAlign: style?.align ?? defaults.textAlign,
3684
+ lineHeight: style?.lineHeight,
3685
+ opacity: style?.opacity ?? defaults.opacity
3686
+ });
3687
+ var resolveScaleInputSliderForRender = (layer, theme, palette) => {
3688
+ const trackFallback = defaultTrackColor(palette);
3689
+ const fillFallback = defaultFillColor(theme, palette);
3690
+ return {
3691
+ showLabels: layer.showLabels !== false,
3692
+ showValue: layer.showValue !== false,
3693
+ trackHeightPx: layer.trackHeight ?? 4,
3694
+ trackColor: resolveColor(theme, palette, layer.trackColor, trackFallback),
3695
+ fillColor: resolveColor(theme, palette, layer.fillColor, fillFallback),
3696
+ thumbSizePx: layer.thumbSize ?? 16,
3697
+ thumbColor: resolveColor(theme, palette, layer.thumbColor, fillFallback),
3698
+ label: resolveTextStyle(layer.labelStyle, theme, palette, {
3699
+ fontSizePx: 11,
3700
+ color: defaultLabelColor(palette),
3701
+ opacity: 0.75
3702
+ }),
3703
+ value: resolveTextStyle(layer.valueStyle, theme, palette, {
3704
+ fontSizePx: 14,
3705
+ fontWeight: 600,
3706
+ color: defaultValueColor(palette),
3707
+ opacity: 1,
3708
+ textAlign: "center"
3709
+ })
3710
+ };
3711
+ };
3712
+
3713
+ // src/layout/scalarLayoutDefaults.ts
3714
+ var DEFAULT_PROGRESS_LINEAR_HEIGHT_PX = 6;
3715
+ var DEFAULT_LOADER_LINEAR_HEIGHT_PX = 6;
3716
+ var DEFAULT_LOADER_CIRCULAR_SIZE_PX = 48;
3717
+ var DEFAULT_LOADER_STROKE_WIDTH_PX = 4;
3718
+ var GAP_BY_KIND = {
3719
+ stack: 12,
3720
+ single_choice: 8,
3721
+ multiple_choice: 8,
3722
+ oauth_login: 8,
3723
+ email_password_auth: 8,
3724
+ email_password_submit: 8,
3725
+ button: 8,
3726
+ back_button: 8,
3727
+ oauth_provider: 8,
3728
+ hyperlink: 0
3729
+ };
3730
+ var defaultGapForLayerKind = (kind) => GAP_BY_KIND[kind] ?? null;
3731
+ var defaultFeedbackStyleScalars = (kind, variant) => {
3732
+ if (kind === "progress") {
3733
+ return { height: DEFAULT_PROGRESS_LINEAR_HEIGHT_PX };
3734
+ }
3735
+ if (variant === "circular") {
3736
+ return {
3737
+ width: DEFAULT_LOADER_CIRCULAR_SIZE_PX,
3738
+ height: DEFAULT_LOADER_CIRCULAR_SIZE_PX,
3739
+ strokeWidth: DEFAULT_LOADER_STROKE_WIDTH_PX
3740
+ };
3741
+ }
3742
+ return { height: DEFAULT_LOADER_LINEAR_HEIGHT_PX };
3743
+ };
3744
+
3745
+ // src/responsive/breakpoints.ts
3746
+ var TAILWIND_DEFAULT_BREAKPOINTS = {
3747
+ sm: 640,
3748
+ md: 768,
3749
+ lg: 1024,
3750
+ xl: 1280,
3751
+ "2xl": 1536
3752
+ };
3753
+ var STYLE_BREAKPOINT_MERGE_ORDER = ["sm", "md", "lg", "xl", "2xl"];
3754
+ var SCREEN_SIZE_BUCKET_ORDER = [
3755
+ "default",
3756
+ "sm",
3757
+ "md",
3758
+ "lg",
3759
+ "xl",
3760
+ "2xl"
3761
+ ];
3762
+ var BUILDER_INSPECTOR_BUCKET_ORDER = [
3763
+ "default",
3764
+ "sm",
3765
+ "md",
3766
+ "lg"
3767
+ ];
3768
+ var SCREEN_SIZE_BUCKET_LABEL = {
3769
+ default: "Default (< 640px)",
3770
+ sm: "sm (640\u2013767px)",
3771
+ md: "md (768\u20131023px)",
3772
+ lg: "lg (1024\u20131279px)",
3773
+ xl: "xl (1280\u20131535px)",
3774
+ "2xl": "2xl (1536px+)"
3775
+ };
3776
+ var DEFAULT_PREVIEW_VIEWPORT_WIDTH_PX = 390;
3777
+ var getScreenSizeBucketForWidth = (width) => {
3778
+ const { sm, md, lg, xl, "2xl": xxl } = TAILWIND_DEFAULT_BREAKPOINTS;
3779
+ if (width < sm) return "default";
3780
+ if (width < md) return "sm";
3781
+ if (width < lg) return "md";
3782
+ if (width < xl) return "lg";
3783
+ if (width < xxl) return "xl";
3784
+ return "2xl";
3785
+ };
3786
+ var getActiveStyleBreakpointChain = (widthPx) => {
3787
+ const bucket = getScreenSizeBucketForWidth(widthPx);
3788
+ if (bucket === "default") return [];
3789
+ const idx = STYLE_BREAKPOINT_MERGE_ORDER.indexOf(bucket);
3790
+ if (idx < 0) return [];
3791
+ return STYLE_BREAKPOINT_MERGE_ORDER.slice(0, idx + 1);
3792
+ };
3793
+
3794
+ // src/responsive/merge.ts
3795
+ var isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
3796
+ var deepMergeStyle2 = (base, ...overrides) => {
3797
+ let acc = base ? { ...base } : void 0;
3798
+ for (const o of overrides) {
3799
+ if (!o) continue;
3800
+ acc = acc ?? {};
3801
+ for (const [k, v] of Object.entries(o)) {
3802
+ if (v === void 0) continue;
3803
+ const prev = acc[k];
3804
+ if (isPlainObject(v) && isPlainObject(prev)) {
3805
+ const merged = deepMergeStyle2(prev, v);
3806
+ if (merged !== void 0) acc[k] = merged;
3807
+ } else {
3808
+ acc[k] = v;
3809
+ }
3810
+ }
3811
+ }
3812
+ return acc;
3813
+ };
3814
+ var mergeResponsivePartial = (base, breakpoints, widthPx) => {
3815
+ const chain = getActiveStyleBreakpointChain(widthPx);
3816
+ if (chain.length === 0) return base ? { ...base } : void 0;
3817
+ let acc = base ? { ...base } : void 0;
3818
+ for (const key of chain) {
3819
+ const patch = breakpoints?.[key];
3820
+ if (!patch) continue;
3821
+ acc = deepMergeStyle2(acc, patch);
3822
+ }
3823
+ return acc;
3824
+ };
3825
+ var mergeResponsivePartialUpToBucket = (base, breakpoints, bucket) => {
3826
+ if (bucket === "default") return base ? { ...base } : void 0;
3827
+ const end = STYLE_BREAKPOINT_MERGE_ORDER.indexOf(bucket);
3828
+ if (end < 0) return base ? { ...base } : void 0;
3829
+ let acc = base ? { ...base } : void 0;
3830
+ for (let i = 0; i <= end; i++) {
3831
+ const key = STYLE_BREAKPOINT_MERGE_ORDER[i];
3832
+ const patch = breakpoints?.[key];
3833
+ if (!patch) continue;
3834
+ acc = deepMergeStyle2(acc, patch);
3835
+ }
3836
+ return acc;
3837
+ };
3838
+
3839
+ // src/responsive/layerResolve.ts
3840
+ var resolveLayerGap = (kind, gap) => gap ?? defaultGapForLayerKind(kind) ?? 0;
3841
+ var resolveProgressLinearHeightPx = (height) => typeof height === "number" ? height : DEFAULT_PROGRESS_LINEAR_HEIGHT_PX;
3842
+ var resolveLoaderLinearHeightPx = (height) => typeof height === "number" ? height : DEFAULT_LOADER_LINEAR_HEIGHT_PX;
3843
+ var resolveLoaderCircularSizePx = (width) => typeof width === "number" ? width : DEFAULT_LOADER_CIRCULAR_SIZE_PX;
3844
+ var resolveLoaderStrokeWidthPx = (strokeWidth) => strokeWidth ?? DEFAULT_LOADER_STROKE_WIDTH_PX;
3845
+ var resolveCommonStyleAtWidth = (base, breakpoints, widthPx) => mergeResponsivePartial(
3846
+ base,
3847
+ breakpoints,
3848
+ widthPx
3849
+ );
3850
+ var resolveTextStyleAtWidth = (base, breakpoints, widthPx) => mergeResponsivePartial(
3851
+ base,
3852
+ breakpoints,
3853
+ widthPx
3854
+ );
3855
+ var resolveImageStyleAtWidth = (base, breakpoints, widthPx) => mergeResponsivePartial(
3856
+ base,
3857
+ breakpoints,
3858
+ widthPx
3859
+ );
3860
+ var resolveIconStyleAtWidth = (base, breakpoints, widthPx) => mergeResponsivePartial(
3861
+ base,
3862
+ breakpoints,
3863
+ widthPx
3864
+ );
3865
+ var resolveButtonStyleAtWidth = (base, breakpoints, widthPx) => mergeResponsivePartial(
3866
+ base,
3867
+ breakpoints,
3868
+ widthPx
3869
+ );
3870
+ var mergeLayoutScalars = (base, breakpoints, widthPx) => {
3871
+ const chain = getActiveStyleBreakpointChain(widthPx);
3872
+ let acc = { ...base };
3873
+ for (const key of chain) {
3874
+ const p = breakpoints?.[key];
3875
+ if (!p) continue;
3876
+ acc = { ...acc, ...p };
3877
+ }
3878
+ return acc;
3879
+ };
3880
+ var resolveStackLayoutAtWidth = (layer, widthPx) => {
3881
+ const base = {
3882
+ gap: layer.gap,
3883
+ direction: layer.direction
3884
+ };
3885
+ const merged = mergeLayoutScalars(
3886
+ base,
3887
+ layer.stackLayoutBreakpoints,
3888
+ widthPx
3889
+ );
3890
+ return {
3891
+ gap: merged.gap,
3892
+ direction: merged.direction ?? layer.direction
3893
+ };
3894
+ };
3895
+ var resolveButtonLayoutAtWidth = (layer, widthPx) => {
3896
+ const merged = mergeLayoutScalars(
3897
+ { gap: layer.gap, direction: layer.direction },
3898
+ layer.buttonLayoutBreakpoints,
3899
+ widthPx
3900
+ );
3901
+ return { gap: merged.gap, direction: merged.direction };
3902
+ };
3903
+ var resolveTextStyleForEditBucket = (base, breakpoints, bucket) => mergeResponsivePartialUpToBucket(
3904
+ base,
3905
+ breakpoints,
3906
+ bucket
3907
+ );
3908
+ var resolveCommonStyleForEditBucket = (base, breakpoints, bucket) => mergeResponsivePartialUpToBucket(
3909
+ base,
3910
+ breakpoints,
3911
+ bucket
3912
+ );
3913
+
3914
+ // src/choiceOptionSelection.ts
3915
+ var CHOICE_OPTION_SELECTED_ICON_SUFFIX = "_sel";
3916
+ var CHOICE_OPTION_UNSELECTED_ICON_SUFFIX = "_unsel";
3917
+ var stackWithSelectedStyle = (stack, isSelected, widthPx) => {
3918
+ if (!isSelected || !stack.selectedStyle) return stack;
3919
+ const resolved = resolveCommonStyleAtWidth(stack.style, stack.styleBreakpoints, widthPx);
3920
+ return {
3921
+ ...stack,
3922
+ style: { ...resolved ?? {}, ...stack.selectedStyle },
3923
+ styleBreakpoints: void 0
3924
+ };
3925
+ };
3926
+ var iconVisibilityForChoiceOption = (layer, isSelected) => {
3927
+ const baseOpacity = layer.style?.opacity ?? 1;
3928
+ if (layer.id.endsWith(CHOICE_OPTION_SELECTED_ICON_SUFFIX)) {
3929
+ return {
3930
+ ...layer,
3931
+ style: { ...layer.style, opacity: isSelected ? baseOpacity : 0 }
3932
+ };
3933
+ }
3934
+ if (layer.id.endsWith(CHOICE_OPTION_UNSELECTED_ICON_SUFFIX)) {
3935
+ return {
3936
+ ...layer,
3937
+ style: { ...layer.style, opacity: isSelected ? 0 : baseOpacity }
3938
+ };
3939
+ }
3940
+ return layer;
3941
+ };
3942
+ var mapChoiceOptionChildForSelection = (child, isSelected, widthPx) => {
3943
+ if (child.kind === "stack") {
3944
+ const stack = child;
3945
+ const nestedSelected = isSelected && !!stack.selectedStyle;
3946
+ const styled = stackWithSelectedStyle(stack, nestedSelected, widthPx);
3947
+ const children = styled.children?.map(
3948
+ (c) => mapChoiceOptionChildForSelection(c, isSelected, widthPx)
3949
+ );
3950
+ return children ? { ...styled, children } : styled;
3951
+ }
3952
+ if (child.kind === "icon") {
3953
+ return iconVisibilityForChoiceOption(child, isSelected);
3954
+ }
3955
+ return child;
3956
+ };
3957
+ var applyChoiceOptionSelectionToStack = (stack, isSelected, widthPx) => {
3958
+ const styled = stackWithSelectedStyle(stack, isSelected, widthPx);
3959
+ if (!styled.children) return styled;
3960
+ return {
3961
+ ...styled,
3962
+ children: styled.children.map((c) => mapChoiceOptionChildForSelection(c, isSelected, widthPx))
3963
+ };
3964
+ };
3965
+
3966
+ // src/layout/authoringLayoutDefaults.ts
3967
+ var LAYOUT_FULL_HUG = { width: "full", height: "auto" };
3968
+ var LAYOUT_FULL_FILL = { width: "full", height: "fill" };
3969
+ var LAYOUT_HUG_HUG = { width: "auto", height: "auto" };
3970
+ var LAYOUT_FULL_FIXED_H160 = { width: "full", height: 160 };
3971
+ var LAYOUT_PROGRESS = {
3972
+ width: "full",
3973
+ height: DEFAULT_PROGRESS_LINEAR_HEIGHT_PX
3974
+ };
3975
+ var LAYOUT_LOADER_LINEAR = {
3976
+ width: "full",
3977
+ height: DEFAULT_LOADER_LINEAR_HEIGHT_PX
3978
+ };
3979
+ var defaultLayoutStyleForKind = (kind) => {
3980
+ switch (kind) {
3981
+ case "stack":
3982
+ return LAYOUT_FULL_FILL;
3983
+ case "text":
3984
+ case "counter":
3985
+ return LAYOUT_HUG_HUG;
3986
+ case "hyperlink":
3987
+ return LAYOUT_HUG_HUG;
3988
+ case "image":
3989
+ case "lottie":
3990
+ case "video":
3991
+ return LAYOUT_FULL_FIXED_H160;
3992
+ case "icon":
3993
+ return LAYOUT_HUG_HUG;
3994
+ case "button":
3995
+ case "back_button":
3996
+ case "oauth_provider":
3997
+ case "email_password_submit":
3998
+ return LAYOUT_FULL_HUG;
3999
+ case "text_input":
4000
+ case "scale_input":
4001
+ case "email_password_auth":
4002
+ case "email_password_field":
4003
+ case "oauth_login":
4004
+ return LAYOUT_FULL_FILL;
4005
+ case "progress":
4006
+ return LAYOUT_PROGRESS;
4007
+ case "loader":
4008
+ return LAYOUT_LOADER_LINEAR;
4009
+ case "checkbox":
4010
+ case "single_choice":
4011
+ case "multiple_choice":
4012
+ return LAYOUT_FULL_HUG;
4013
+ case "carousel":
4014
+ return null;
4015
+ default:
4016
+ return null;
4017
+ }
4018
+ };
4019
+ var mergeLayoutDefaultsIntoStyle = (kind, style) => {
4020
+ const defaults = defaultLayoutStyleForKind(kind);
4021
+ if (!defaults) return style ?? {};
4022
+ return { ...defaults, ...style };
4023
+ };
4024
+
4025
+ // src/layout/normalizeLayerLayout.ts
4026
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4027
+ var applyAxisDefaultsInPlace = (layer, kind) => {
4028
+ const current = isObj(layer.style) ? layer.style : void 0;
4029
+ if (kind === "loader" && layer.variant === "circular") {
4030
+ const feedback = defaultFeedbackStyleScalars("loader", "circular");
4031
+ layer.style = { ...feedback, ...current ?? {} };
4032
+ return;
4033
+ }
4034
+ if (!defaultLayoutStyleForKind(kind)) return;
4035
+ layer.style = mergeLayoutDefaultsIntoStyle(kind, current);
4036
+ };
4037
+ var applyGapDefaultInPlace = (layer, kind) => {
4038
+ const gapDefault = defaultGapForLayerKind(kind);
4039
+ if (gapDefault === null) return;
4040
+ if (kind === "oauth_provider" && layer.variant !== "custom") return;
4041
+ if (layer.gap === void 0) layer.gap = gapDefault;
4042
+ };
4043
+ var applyLayoutDefaultsToLayerInPlace = (layer) => {
4044
+ if (typeof layer.kind !== "string") return;
4045
+ const kind = layer.kind;
4046
+ applyAxisDefaultsInPlace(layer, kind);
4047
+ applyGapDefaultInPlace(layer, kind);
4048
+ };
4049
+ var recurseChildren = (parent, key) => {
4050
+ const kids = parent[key];
4051
+ if (!Array.isArray(kids)) return;
4052
+ for (const child of kids) {
4053
+ if (isObj(child)) normalizeLayerLayoutInPlace(child);
4054
+ }
4055
+ };
4056
+ var normalizeLayerLayoutInPlace = (layer) => {
4057
+ applyLayoutDefaultsToLayerInPlace(layer);
4058
+ const kind = layer.kind;
4059
+ if (kind === "carousel") {
4060
+ recurseChildren(layer, "slides");
4061
+ return;
4062
+ }
4063
+ recurseChildren(layer, "children");
4064
+ };
4065
+ var normalizeManifestLayoutInPlace = (manifest) => {
4066
+ const screens = manifest.screens;
4067
+ if (!Array.isArray(screens)) return;
4068
+ for (const screen of screens) {
4069
+ if (!isObj(screen)) continue;
4070
+ const regions = screen.regions;
4071
+ if (!isObj(regions)) continue;
4072
+ for (const key of ["header", "body", "footer"]) {
4073
+ const region = regions[key];
4074
+ if (isObj(region)) normalizeLayerLayoutInPlace(region);
4075
+ }
4076
+ }
4077
+ };
4078
+
4079
+ // src/layout/stackFlexGrow.ts
4080
+ var stackMainAxisFillHeight = (height) => height === "fill" || height === "full";
4081
+
4082
+ // src/responsive/screenContainerResolve.ts
4083
+ var mergeBackgroundFillPatch = (fill, patch) => {
4084
+ if (fill.kind === "color") {
4085
+ return {
4086
+ ...fill,
4087
+ kind: "color",
4088
+ ...patch.color !== void 0 ? { color: patch.color } : {},
4089
+ ...patch.opacity !== void 0 ? { opacity: patch.opacity } : {}
4090
+ };
4091
+ }
4092
+ const scrim = patch.scrim !== void 0 ? deepMergeStyle2(fill.scrim, patch.scrim) : fill.scrim;
4093
+ const shared = {
4094
+ ...fill,
4095
+ ...patch.fit !== void 0 ? { fit: patch.fit } : {},
4096
+ ...patch.opacity !== void 0 ? { opacity: patch.opacity } : {},
4097
+ ...scrim !== void 0 ? { scrim } : {}
4098
+ };
4099
+ if (fill.kind === "image") {
4100
+ return shared;
4101
+ }
4102
+ return {
4103
+ ...shared,
4104
+ ...patch.loop !== void 0 ? { loop: patch.loop } : {},
4105
+ ...patch.autoPlay !== void 0 ? { autoPlay: patch.autoPlay } : {},
4106
+ ...patch.triggerLayerId !== void 0 ? { triggerLayerId: patch.triggerLayerId } : {},
4107
+ ...patch.onComplete !== void 0 ? { onComplete: patch.onComplete } : {},
4108
+ ...patch.audioEnabled !== void 0 ? { audioEnabled: patch.audioEnabled } : {}
4109
+ };
4110
+ };
4111
+ var resolveScreenContainerStyleAtWidth = (base, breakpoints, widthPx) => {
4112
+ const chain = getActiveStyleBreakpointChain(widthPx);
4113
+ let acc = base ? { ...base } : void 0;
4114
+ for (const key of chain) {
4115
+ const patch = breakpoints?.[key];
4116
+ if (!patch) continue;
4117
+ acc = acc ?? {};
4118
+ if (patch.padding !== void 0) acc = { ...acc, padding: patch.padding };
4119
+ if (patch.margin !== void 0) acc = { ...acc, margin: patch.margin };
4120
+ if (patch.insetSafeArea !== void 0) acc = { ...acc, insetSafeArea: patch.insetSafeArea };
4121
+ if (patch.backgroundFillPatch && acc.backgroundFill) {
4122
+ acc = {
4123
+ ...acc,
4124
+ backgroundFill: mergeBackgroundFillPatch(acc.backgroundFill, patch.backgroundFillPatch)
4125
+ };
4126
+ }
4127
+ }
4128
+ return acc;
4129
+ };
4130
+ var screenBackgroundFillUsesMedia = (fill) => fill?.kind === "image" || fill?.kind === "video";
4131
+ var resolveScreenContainerForEditBucket = (base, breakpoints, bucket) => {
4132
+ if (bucket === "default") return base ? { ...base } : void 0;
4133
+ const end = STYLE_BREAKPOINT_MERGE_ORDER.indexOf(bucket);
4134
+ if (end < 0) return base ? { ...base } : void 0;
4135
+ let acc = base ? { ...base } : {};
4136
+ for (let i = 0; i <= end; i++) {
4137
+ const patch = breakpoints?.[STYLE_BREAKPOINT_MERGE_ORDER[i]];
4138
+ if (!patch) continue;
4139
+ acc = acc ?? {};
4140
+ if (patch.padding !== void 0) acc = { ...acc, padding: patch.padding };
4141
+ if (patch.margin !== void 0) acc = { ...acc, margin: patch.margin };
4142
+ if (patch.insetSafeArea !== void 0) acc = { ...acc, insetSafeArea: patch.insetSafeArea };
4143
+ if (patch.backgroundFillPatch && acc.backgroundFill) {
4144
+ acc = {
4145
+ ...acc,
4146
+ backgroundFill: mergeBackgroundFillPatch(acc.backgroundFill, patch.backgroundFillPatch)
4147
+ };
4148
+ }
4149
+ }
4150
+ const hasKeys = acc && (acc.padding !== void 0 || acc.margin !== void 0 || acc.insetSafeArea !== void 0 || acc.backgroundFill !== void 0);
4151
+ return hasKeys ? acc : void 0;
4152
+ };
4153
+
4154
+ // src/responsive/previewSafeAreaInsets.ts
4155
+ var previewPhoneSafeAreaInsetTopPx = (width) => {
4156
+ const statusChromePadY = Math.max(6, Math.round(width * 0.016));
4157
+ const chromePadBottom = Math.max(4, Math.round(width * 0.01));
4158
+ const padY = Math.max(5, Math.round(width * 0.012));
4159
+ const statusH = Math.max(23, Math.round(width * 0.058));
4160
+ return statusChromePadY + statusH + 2 * padY + chromePadBottom;
4161
+ };
4162
+ var previewPhoneSafeAreaInsetBottomPx = (width, systemUi = "ios") => {
4163
+ const pad = systemUi === "ios" ? 8 : 10;
4164
+ const pill = systemUi === "ios" ? 5 : 4;
4165
+ return pad + pill + Math.max(6, Math.round(width * 0.014));
4166
+ };
4167
+ var previewPhoneSafeAreaInsetHorizontalPx = (_width) => 0;
4168
+ var previewPhoneSafeAreaPadding = (widthPx, systemUi = "ios") => ({
4169
+ t: previewPhoneSafeAreaInsetTopPx(widthPx),
4170
+ r: previewPhoneSafeAreaInsetHorizontalPx(),
4171
+ b: previewPhoneSafeAreaInsetBottomPx(widthPx, systemUi),
4172
+ l: previewPhoneSafeAreaInsetHorizontalPx()
4173
+ });
4174
+
4175
+ // src/responsive/screenShellInsets.ts
4176
+ var edge = (a, b) => {
4177
+ const sum = (a ?? 0) + (b ?? 0);
4178
+ return sum === 0 ? void 0 : sum;
4179
+ };
4180
+ var addPadding = (a, b) => {
4181
+ if (!a && !b) return void 0;
4182
+ const out = {
4183
+ t: edge(a?.t, b?.t),
4184
+ r: edge(a?.r, b?.r),
4185
+ b: edge(a?.b, b?.b),
4186
+ l: edge(a?.l, b?.l)
4187
+ };
4188
+ const cleaned = Object.fromEntries(
4189
+ Object.entries(out).filter(([, v]) => v !== void 0)
4190
+ );
4191
+ return Object.keys(cleaned).length === 0 ? void 0 : cleaned;
4192
+ };
4193
+ var resolveEffectiveScreenShellPadding = (opts) => {
4194
+ if (!opts.insetSafeArea) return opts.manual;
4195
+ return addPadding(opts.manual, opts.safeAreaInsets);
4196
+ };
4197
+
4198
+ export { AI_FLOW_PLACEHOLDER_SCREEN_ID, BRAND_GRADIENT_PREFIX, BUILDER_INSPECTOR_BUCKET_ORDER, BUILDER_RULES_AGENT_BULLETS, CHOICE_OPTION_SELECTED_ICON_SUFFIX, CHOICE_OPTION_UNSELECTED_ICON_SUFFIX, DEFAULT_LOADER_CIRCULAR_SIZE_PX, DEFAULT_LOADER_LINEAR_HEIGHT_PX, DEFAULT_LOADER_STROKE_WIDTH_PX, DEFAULT_LOTTIE_PLAY_DURATION_MS, DEFAULT_PREVIEW_VIEWPORT_WIDTH_PX, DEFAULT_PROGRESS_LINEAR_HEIGHT_PX, DEFAULT_WORKBENCH_TIMELINE_MS, EASING_BEZIERS, LAYER_ROTATE_DEG_MAX, LAYER_ROTATE_DEG_MIN, LEGACY_RESTING_MOTION_ID, MIN_ANIMATION_TIMELINE_AUTHORING_SPAN_MS, RESTING_MOTION_BOUNCE_BASE_PX, RESTING_MOTION_DEFAULT_DURATION_MS, RESTING_MOTION_DEFAULT_ROTATE_DEG, RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT, RESTING_MOTION_DEFAULT_SCALE_PERCENT, RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT, RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT, RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX, RESTING_MOTION_DEFAULT_TRANSLATE_RANGE_PX, RESTING_MOTION_KEYFRAMES_CSS, RESTING_MOTION_PULSE_DIP_BASE, RESTING_MOTION_SEGMENT_DURATION_MS_MAX, RESTING_MOTION_SEGMENT_DURATION_MS_MIN, SCREEN_SIZE_BUCKET_LABEL, SCREEN_SIZE_BUCKET_ORDER, STYLE_BREAKPOINT_MERGE_ORDER, TAILWIND_DEFAULT_BREAKPOINTS, TEXT_FONT_FAMILY_SYSTEM_UI, WEB_DOCUMENT_FONT_STACK, abandonFlow, addPadding, analyzeLiquidTemplateInner, animationTimelineAuthoringEndMs, appendGeneratedScreenToManifest, applyChoiceOptionSelectionToStack, applyGraphLanding, applyLayoutDefaultsToLayerInPlace, applyReducedMotion, applyStaggerIndicesFromTreeOrder, assignVariant, bouncePhaseToTranslateY, brandGradientFromThemedColor, brandGradientNativeLinear, brandGradientSolidFallback, brandGradientToCss, brandingWebFontFacesCss, buildBrandingFontLoadMap, buildCompletionResponses, buildFlowPreview, buildLinearGradientCss, buildTwoStopLinearGradientCss, buttonPaletteBorderFallback, buttonVariantAuthoringStyleDefaults, buttonVariantChromeForTheme, choiceOptionLabel, clampRestingMotionSegmentDurationMs, clipsByLayerId, cloneManifestForBillingSlice, collapseManifestToDefaultLocaleOnly, collectBrandGradientManifestIssues, collectDecisionWarnings, collectFieldKeys, collectFlowBuilderIssues, collectInterpolationWarnings, collectManifestLayerIds, computeDominators, decisionTargetIds, deepMergeStyle2 as deepMergeStyle, defaultFeedbackStyleScalars, defaultGapForLayerKind, defaultLayoutStyleForKind, digestDecisionExpression, dropShadowToBoxShadow, dropShadowToNativeStyle, dropShadowToWebStyle, effectiveDelayMs, ensureLayoutNodes, evaluateDecisionExpression, evaluateDecisionNode, externalSurfaceResponseKey, extractLiquidTemplateBodies, extractTranslationPlaceholders, findDecisionNode, findDefaultPathTailScreen, findExternalSurface, findInputLayer, findLayerById, findManualSubmitInputLayer, findOptionStackForChoice, findScreen, fnv1a, formatCounterAsTime, formatCounterLayerDisplay, formatCounterLayerValue, getActiveStyleBreakpointChain, getScreenSizeBucketForWidth, iconLayerHasAuthoringColor, initFlowState, insertScreenAfterAnchorInManifest, interpolateTemplateString, isAiFlowPlaceholderManifest, isAuthTerminalExportResponseKey, isBrandGradientToken, isEligibleConsumedDraft, isPropertyAllowedOnLayer, isStoredLinearGradientCss, layerHasAnimationClips, layerMountClipsEndMs, layerRestingMotionEndMs, layerRestingMotionEntries, layerRestingMotionStartMs, layerRotateCssTransform, layerRotateNativeTransform, layerWithRestingMotionEntries, listAnimatablePropsForLayerKind, loaderFillProgressAtGlobalMs, loaderLayerFillTimelineEndMs, lottieJsonDurationMs, manifestHasScreenAnimations, manifestUsesExternalIntegrations, manifestUsesTranslationsBeyondDefault, mergeAiGeneratedScreenIntoManifest, mergeAuthoringButtonStyleWithLayer, mergeCheckboxGlyphStyle, mergeLayoutDefaultsIntoStyle, mergePartsForTimeFormat, mergeResponsivePartial, mergeResponsivePartialUpToBucket, mergeRheoAgentPatchIntoDraft, mergeSampledClips, mergeScaleInputLabelStyle, mergeScaleInputValueStyle, mergeSlimManifestPreservingBuilderMeta, migrateStaggerClipsToMount, motionTimelineScrubClampMs, multiplyColorAlpha, nativeBrandBackgroundFromThemedColor, nativeFontRegistrationNameForStyle, nativeLinearFromAngleAndStops, nativeLinearFromAngleAndTwoColors, nextUniqueFieldKey, normalizeHexForGradient, normalizeLayerLayoutInPlace, normalizeManifestLayoutInPlace, parseLinearGradientCss, parseTwoStopLinearGradientCss, previewPhoneSafeAreaInsetBottomPx, previewPhoneSafeAreaInsetHorizontalPx, previewPhoneSafeAreaInsetTopPx, previewPhoneSafeAreaPadding, remapReplacedScreenLayerCollisionsInManifest, remapScreenIdsForManifestIngest, resolveAndInterpolateLocalizedText, resolveBrandGradientToken, resolveButtonLayoutAtWidth, resolveButtonStyleAtWidth, resolveCheckboxGlyphForRender, resolveCommonStyleAtWidth, resolveCommonStyleForEditBucket, resolveCounterAnimationDurationMs, resolveEffectiveScreenShellPadding, resolveHyperlinkPreviewLabel, resolveIconStyleAtWidth, resolveImageStyleAtWidth, resolveLayerGap, resolveLoaderCircularSizePx, resolveLoaderLinearHeightPx, resolveLoaderStrokeWidthPx, resolveNativeTextFontFamilyName, resolveNextScreenId, resolveProgressLinearHeightPx, resolveScaleInputSliderForRender, resolveScreenContainerForEditBucket, resolveScreenContainerStyleAtWidth, resolveStackLayoutAtWidth, resolveTextStyleAtWidth, resolveTextStyleForEditBucket, resolveThemedBackground, resolveThemedColor, resolveThroughGraph, resolveTokens, resolveWebRootFontFamilyCss, resolveWebTextFontFamilyCss, responseKeyFor, restingMotionAllowedAtTime, restingMotionBounceAmplitudePx, restingMotionCycleDurationMs, restingMotionDelayAfterMountEndMs, restingMotionEffectiveDurationMs, restingMotionIntensity, restingMotionLocalElapsedMs, restingMotionMotionSpeedSliderMs, restingMotionNormalizeScaleNonLoopClip, restingMotionPhase01, restingMotionPulseMinOpacity, restingMotionRotateDirection, restingMotionRotateMaxDeg, restingMotionRotateSign, restingMotionRotateSpringBack, restingMotionSampleStyle, restingMotionScaleAmountFraction, restingMotionScaleAtPhase, restingMotionScaleDirection, restingMotionScaleDownFraction, restingMotionScalePatternDurationMs, restingMotionScalePeakMultiplier, restingMotionScalePercentResolved, restingMotionScaleSpringBack, restingMotionScaleUpFraction, restingMotionStyleAtTime, restingMotionSyncScaleNonLoopClipAndPattern, restingMotionTranslateAuthoringPeakPercent, restingMotionTranslateAuthoringPeakPx, restingMotionTranslatePeakPx, restingMotionTranslatePeakResolved, restingMotionTranslateSpringBack, restingMotionWebStyle, sampleClipAt, sampleLayerAnimAt, sampleTrack, scaleStep, scaleValueInRange, scaleValueIsOnStep, screenAnimationsDurationMs, screenBackgroundFillUsesMedia, screenHasContinueButton, screenLoaderTimelineExtentMs, screenRestingTimelineExtentMs, snapScaleValue, splitSecondsForTimeFormat, stackMainAxisFillHeight, stackWithSelectedStyle, startFlow, stepResponseToCompletionValue, stripAuthResponsesForTerminalExport, stripManifestAnimations, stripManifestMotion, submitResponse, textLayerHasAuthoringColor, translationPlaceholdersMatch, upstreamFieldKeysForPicker, upstreamScreenIdsForPicker, validateEmailPasswordAuthFields, validateManifest, validatePublishable, validateTextInputValue, walkLayers, walkScreen, workbenchTimelineTotalMs };
4199
+ //# sourceMappingURL=index.js.map
4200
+ //# sourceMappingURL=index.js.map