@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.
- package/dist/agentPrompt/index.d.ts +72 -0
- package/dist/agentPrompt/index.js +739 -0
- package/dist/agentPrompt/index.js.map +1 -0
- package/dist/aiFlowGenerationMerge.d.ts +32 -0
- package/dist/aiFlowGenerationMerge.js +120 -0
- package/dist/aiFlowGenerationMerge.js.map +1 -0
- package/dist/animations.d.ts +110 -0
- package/dist/animations.js +312 -0
- package/dist/animations.js.map +1 -0
- package/dist/assignment.d.ts +7 -0
- package/dist/assignment.js +25 -0
- package/dist/assignment.js.map +1 -0
- package/dist/brandGradient.d.ts +57 -0
- package/dist/brandGradient.js +137 -0
- package/dist/brandGradient.js.map +1 -0
- package/dist/brandGradientManifestIssues.d.ts +11 -0
- package/dist/brandGradientManifestIssues.js +302 -0
- package/dist/brandGradientManifestIssues.js.map +1 -0
- package/dist/buildFlowPreview.d.ts +7 -0
- package/dist/buildFlowPreview.js +81 -0
- package/dist/buildFlowPreview.js.map +1 -0
- package/dist/buttonVariantChrome.d.ts +26 -0
- package/dist/buttonVariantChrome.js +59 -0
- package/dist/buttonVariantChrome.js.map +1 -0
- package/dist/checkboxGlyphStyle.d.ts +31 -0
- package/dist/checkboxGlyphStyle.js +241 -0
- package/dist/checkboxGlyphStyle.js.map +1 -0
- package/dist/choiceOptionSelection.d.ts +11 -0
- package/dist/choiceOptionSelection.js +120 -0
- package/dist/choiceOptionSelection.js.map +1 -0
- package/dist/colorAlpha.d.ts +8 -0
- package/dist/colorAlpha.js +48 -0
- package/dist/colorAlpha.js.map +1 -0
- package/dist/counterLayer.d.ts +42 -0
- package/dist/counterLayer.js +95 -0
- package/dist/counterLayer.js.map +1 -0
- package/dist/decisionEval.d.ts +27 -0
- package/dist/decisionEval.js +197 -0
- package/dist/decisionEval.js.map +1 -0
- package/dist/dropShadow.d.ts +26 -0
- package/dist/dropShadow.js +76 -0
- package/dist/dropShadow.js.map +1 -0
- package/dist/emailPasswordAuthValidation.d.ts +16 -0
- package/dist/emailPasswordAuthValidation.js +25 -0
- package/dist/emailPasswordAuthValidation.js.map +1 -0
- package/dist/flowBuilderRules.d.ts +15 -0
- package/dist/flowBuilderRules.js +368 -0
- package/dist/flowBuilderRules.js.map +1 -0
- package/dist/flowGraph.d.ts +19 -0
- package/dist/flowGraph.js +373 -0
- package/dist/flowGraph.js.map +1 -0
- package/dist/hyperlinkLabel.d.ts +19 -0
- package/dist/hyperlinkLabel.js +232 -0
- package/dist/hyperlinkLabel.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +4200 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolateTemplate.d.ts +44 -0
- package/dist/interpolateTemplate.js +188 -0
- package/dist/interpolateTemplate.js.map +1 -0
- package/dist/layerRotate.d.ts +10 -0
- package/dist/layerRotate.js +9 -0
- package/dist/layerRotate.js.map +1 -0
- package/dist/layerTypography.d.ts +36 -0
- package/dist/layerTypography.js +68 -0
- package/dist/layerTypography.js.map +1 -0
- package/dist/layers.d.ts +69 -0
- package/dist/layers.js +257 -0
- package/dist/layers.js.map +1 -0
- package/dist/layout/index.d.ts +57 -0
- package/dist/layout/index.js +151 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/manifestBillingSlice.d.ts +17 -0
- package/dist/manifestBillingSlice.js +102 -0
- package/dist/manifestBillingSlice.js.map +1 -0
- package/dist/prepareAiGeneratedScreen.d.ts +17 -0
- package/dist/prepareAiGeneratedScreen.js +99 -0
- package/dist/prepareAiGeneratedScreen.js.map +1 -0
- package/dist/publish-exports.json +166 -0
- package/dist/responsive/breakpoints.d.ts +34 -0
- package/dist/responsive/breakpoints.js +52 -0
- package/dist/responsive/breakpoints.js.map +1 -0
- package/dist/responsive/index.d.ts +8 -0
- package/dist/responsive/index.js +307 -0
- package/dist/responsive/index.js.map +1 -0
- package/dist/responsive/layerResolve.d.ts +43 -0
- package/dist/responsive/layerResolve.js +168 -0
- package/dist/responsive/layerResolve.js.map +1 -0
- package/dist/responsive/merge.d.ts +19 -0
- package/dist/responsive/merge.js +74 -0
- package/dist/responsive/merge.js.map +1 -0
- package/dist/responsive/previewSafeAreaInsets.d.ts +14 -0
- package/dist/responsive/previewSafeAreaInsets.js +24 -0
- package/dist/responsive/previewSafeAreaInsets.js.map +1 -0
- package/dist/responsive/screenContainerResolve.d.ts +11 -0
- package/dist/responsive/screenContainerResolve.js +122 -0
- package/dist/responsive/screenContainerResolve.js.map +1 -0
- package/dist/responsive/screenShellInsets.d.ts +11 -0
- package/dist/responsive/screenShellInsets.js +26 -0
- package/dist/responsive/screenShellInsets.js.map +1 -0
- package/dist/restingMotion.d.ts +167 -0
- package/dist/restingMotion.js +484 -0
- package/dist/restingMotion.js.map +1 -0
- package/dist/rheoAgentManifestMerge.d.ts +33 -0
- package/dist/rheoAgentManifestMerge.js +55 -0
- package/dist/rheoAgentManifestMerge.js.map +1 -0
- package/dist/scaleInputStyle.d.ts +35 -0
- package/dist/scaleInputStyle.js +77 -0
- package/dist/scaleInputStyle.js.map +1 -0
- package/dist/scaleValidation.d.ts +9 -0
- package/dist/scaleValidation.js +21 -0
- package/dist/scaleValidation.js.map +1 -0
- package/dist/stateMachine.d.ts +105 -0
- package/dist/stateMachine.js +674 -0
- package/dist/stateMachine.js.map +1 -0
- package/dist/stepResponse-BXgoZ7o-.d.ts +112 -0
- package/dist/textInputValidation.d.ts +14 -0
- package/dist/textInputValidation.js +46 -0
- package/dist/textInputValidation.js.map +1 -0
- package/dist/translationPlaceholders.d.ts +9 -0
- package/dist/translationPlaceholders.js +52 -0
- package/dist/translationPlaceholders.js.map +1 -0
- package/dist/validation.d.ts +31 -0
- package/dist/validation.js +233 -0
- package/dist/validation.js.map +1 -0
- package/package.json +242 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import '@getrheo/contracts/localized';
|
|
2
|
+
import '@getrheo/contracts/layers';
|
|
3
|
+
|
|
4
|
+
// src/restingMotion/restingMotionEntries.ts
|
|
5
|
+
var RESTING_MOTION_DEFAULT_DURATION_MS = {
|
|
6
|
+
translate: 2400,
|
|
7
|
+
bounce: 2e3,
|
|
8
|
+
scale: 2200,
|
|
9
|
+
pulse: 1800,
|
|
10
|
+
rotate: 3200
|
|
11
|
+
};
|
|
12
|
+
var RESTING_MOTION_SEGMENT_DURATION_MS_MIN = 200;
|
|
13
|
+
var RESTING_MOTION_SEGMENT_DURATION_MS_MAX = 2e4;
|
|
14
|
+
var clampRestingMotionSegmentDurationMs = (ms) => Math.min(
|
|
15
|
+
RESTING_MOTION_SEGMENT_DURATION_MS_MAX,
|
|
16
|
+
Math.max(RESTING_MOTION_SEGMENT_DURATION_MS_MIN, Math.round(ms))
|
|
17
|
+
);
|
|
18
|
+
var LEGACY_RESTING_MOTION_ID = "__legacy";
|
|
19
|
+
var layerRestingMotionEntries = (layer) => {
|
|
20
|
+
const list = layer.restingMotions;
|
|
21
|
+
if (list && list.length > 0) return list;
|
|
22
|
+
if (layer.restingMotion) {
|
|
23
|
+
return [{ ...layer.restingMotion, id: LEGACY_RESTING_MOTION_ID }];
|
|
24
|
+
}
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
var layerWithRestingMotionEntries = (layer, entries) => {
|
|
28
|
+
if (entries.length === 0) {
|
|
29
|
+
const next = { ...layer };
|
|
30
|
+
delete next.restingMotion;
|
|
31
|
+
delete next.restingMotions;
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
...layer,
|
|
36
|
+
restingMotions: entries.map((e) => ({ ...e })),
|
|
37
|
+
restingMotion: void 0
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
var restingMotionSyncScaleNonLoopClipAndPattern = (r, ms) => {
|
|
41
|
+
const v = clampRestingMotionSegmentDurationMs(ms);
|
|
42
|
+
return { ...r, durationMs: v, scalePatternDurationMs: v };
|
|
43
|
+
};
|
|
44
|
+
var restingMotionNormalizeScaleNonLoopClip = (r) => {
|
|
45
|
+
if (r.preset !== "scale" || r.loop === true) return r;
|
|
46
|
+
const ms = clampRestingMotionSegmentDurationMs(
|
|
47
|
+
r.durationMs ?? r.scalePatternDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS.scale
|
|
48
|
+
);
|
|
49
|
+
return { ...r, durationMs: ms, scalePatternDurationMs: ms };
|
|
50
|
+
};
|
|
51
|
+
var restingMotionEffectiveDurationMs = (r) => r.durationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];
|
|
52
|
+
var restingMotionCycleDurationMs = (r) => r.cycleDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];
|
|
53
|
+
var restingMotionMotionSpeedSliderMs = (r) => r.loop === true ? restingMotionCycleDurationMs(r) : restingMotionEffectiveDurationMs(r);
|
|
54
|
+
var restingMotionIntensity = (r) => {
|
|
55
|
+
"worklet";
|
|
56
|
+
return r.intensity ?? 1;
|
|
57
|
+
};
|
|
58
|
+
var restingMotionDelayAfterMountEndMs = (r) => r.delayMsAfterMountEnd ?? 0;
|
|
59
|
+
var walkLayers = (root, fn) => {
|
|
60
|
+
const visit = (l, depth) => {
|
|
61
|
+
fn(l, depth);
|
|
62
|
+
if (l.kind === "stack") l.children.forEach((c) => visit(c, depth + 1));
|
|
63
|
+
else if (l.kind === "carousel") l.slides.forEach((c) => visit(c, depth + 1));
|
|
64
|
+
else if (l.kind === "button") l.children.forEach((c) => visit(c, depth + 1));
|
|
65
|
+
else if (l.kind === "back_button") l.children.forEach((c) => visit(c, depth + 1));
|
|
66
|
+
else if (l.kind === "hyperlink") l.children.forEach((c) => visit(c, depth + 1));
|
|
67
|
+
else if (l.kind === "single_choice" || l.kind === "multiple_choice") {
|
|
68
|
+
l.children.forEach((c) => visit(c, depth + 1));
|
|
69
|
+
} else if (l.kind === "text_input" || l.kind === "scale_input") {
|
|
70
|
+
l.children?.forEach((c) => visit(c, depth + 1));
|
|
71
|
+
} else if (l.kind === "oauth_login") {
|
|
72
|
+
l.children.forEach((c) => visit(c, depth + 1));
|
|
73
|
+
} else if (l.kind === "oauth_provider" && l.variant === "custom") {
|
|
74
|
+
l.children.forEach((c) => visit(c, depth + 1));
|
|
75
|
+
} else if (l.kind === "email_password_auth") {
|
|
76
|
+
l.children.forEach((c) => visit(c, depth + 1));
|
|
77
|
+
} else if (l.kind === "email_password_field") {
|
|
78
|
+
l.children?.forEach((c) => visit(c, depth + 1));
|
|
79
|
+
} else if (l.kind === "email_password_submit") {
|
|
80
|
+
l.children.forEach((c) => visit(c, depth + 1));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
visit(root, 0);
|
|
84
|
+
};
|
|
85
|
+
var walkScreen = (screen, fn) => {
|
|
86
|
+
if (screen.regions.header) walkLayers(screen.regions.header, fn);
|
|
87
|
+
walkLayers(screen.regions.body, fn);
|
|
88
|
+
if (screen.regions.footer) walkLayers(screen.regions.footer, fn);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/animations.ts
|
|
92
|
+
var effectiveDelayMs = (clip, screen) => {
|
|
93
|
+
const base = clip.delayMs ?? 0;
|
|
94
|
+
if (clip.trigger === "stagger" && clip.staggerIndex !== void 0) {
|
|
95
|
+
const step = screen.stagger?.stepMs ?? 60;
|
|
96
|
+
return base + clip.staggerIndex * step;
|
|
97
|
+
}
|
|
98
|
+
return base;
|
|
99
|
+
};
|
|
100
|
+
var screenAnimationsDurationMs = (screen) => {
|
|
101
|
+
if (!screen.animations || screen.animations.length === 0) return 0;
|
|
102
|
+
let max = 0;
|
|
103
|
+
for (const clip of screen.animations) {
|
|
104
|
+
const total = effectiveDelayMs(clip, screen) + clip.durationMs;
|
|
105
|
+
if (total > max) max = total;
|
|
106
|
+
}
|
|
107
|
+
return max;
|
|
108
|
+
};
|
|
109
|
+
var loaderLayerFillTimelineEndMs = (layer) => (layer.fillDelayMs ?? 0) + (layer.durationMs ?? 2e3);
|
|
110
|
+
var screenLoaderTimelineExtentMs = (screen) => {
|
|
111
|
+
let max = 0;
|
|
112
|
+
walkScreen(screen, (l) => {
|
|
113
|
+
if (l.kind !== "loader") return;
|
|
114
|
+
const end = loaderLayerFillTimelineEndMs(l);
|
|
115
|
+
if (end > max) max = end;
|
|
116
|
+
});
|
|
117
|
+
return max;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/restingMotion/restingMotionTimeline.ts
|
|
121
|
+
var layerMountClipsEndMs = (screen, layerId) => {
|
|
122
|
+
const clips = screen.animations?.filter(
|
|
123
|
+
(c) => c.targetLayerId === layerId && (c.trigger === "mount" || c.trigger === "stagger")
|
|
124
|
+
) ?? [];
|
|
125
|
+
let maxEnd = 0;
|
|
126
|
+
for (const c of clips) {
|
|
127
|
+
maxEnd = Math.max(maxEnd, effectiveDelayMs(c, screen) + c.durationMs);
|
|
128
|
+
}
|
|
129
|
+
return maxEnd;
|
|
130
|
+
};
|
|
131
|
+
var layerRestingMotionStartMs = (screen, layerId, cfg) => cfg.timelineStartMs !== void 0 ? cfg.timelineStartMs : layerMountClipsEndMs(screen, layerId) + restingMotionDelayAfterMountEndMs(cfg);
|
|
132
|
+
var screenRestingTimelineExtentMs = (screen) => {
|
|
133
|
+
let max = 0;
|
|
134
|
+
walkScreen(screen, (l) => {
|
|
135
|
+
for (const r of layerRestingMotionEntries(l)) {
|
|
136
|
+
const start = layerRestingMotionStartMs(screen, l.id, r);
|
|
137
|
+
max = Math.max(max, start + restingMotionEffectiveDurationMs(r));
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return max;
|
|
141
|
+
};
|
|
142
|
+
var motionTimelineScrubClampMs = (screen) => Math.max(
|
|
143
|
+
screenAnimationsDurationMs(screen),
|
|
144
|
+
screenRestingTimelineExtentMs(screen),
|
|
145
|
+
screenLoaderTimelineExtentMs(screen)
|
|
146
|
+
);
|
|
147
|
+
var DEFAULT_WORKBENCH_TIMELINE_MS = 1e4;
|
|
148
|
+
var workbenchTimelineTotalMs = (screen) => {
|
|
149
|
+
const extent = motionTimelineScrubClampMs(screen);
|
|
150
|
+
return extent > 0 ? extent : DEFAULT_WORKBENCH_TIMELINE_MS;
|
|
151
|
+
};
|
|
152
|
+
var layerRestingMotionEndMs = (screen, layerId, cfg) => layerRestingMotionStartMs(screen, layerId, cfg) + restingMotionEffectiveDurationMs(cfg);
|
|
153
|
+
var restingMotionAllowedAtTime = (screen, layerId, tMs, cfg) => {
|
|
154
|
+
const start = layerRestingMotionStartMs(screen, layerId, cfg);
|
|
155
|
+
const end = start + restingMotionEffectiveDurationMs(cfg);
|
|
156
|
+
return tMs >= start && tMs < end;
|
|
157
|
+
};
|
|
158
|
+
var restingMotionLocalElapsedMs = (screen, layerId, cfg, tMs) => {
|
|
159
|
+
if (!restingMotionAllowedAtTime(screen, layerId, tMs, cfg)) return null;
|
|
160
|
+
return tMs - layerRestingMotionStartMs(screen, layerId, cfg);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/restingMotion/restingMotionEffects.ts
|
|
164
|
+
var RESTING_MOTION_BOUNCE_BASE_PX = 14;
|
|
165
|
+
var restingMotionBounceAmplitudePx = (r) => r.bounceAmplitudePx ?? RESTING_MOTION_BOUNCE_BASE_PX * restingMotionIntensity(r);
|
|
166
|
+
var bouncePhaseToTranslateY = (ph, amplitudePx) => -amplitudePx * Math.sin(Math.PI * ph);
|
|
167
|
+
var RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT = 8;
|
|
168
|
+
var RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT = 0;
|
|
169
|
+
var RESTING_MOTION_DEFAULT_SCALE_PERCENT = RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;
|
|
170
|
+
var RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT = 6;
|
|
171
|
+
var RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX = 6;
|
|
172
|
+
var RESTING_MOTION_DEFAULT_TRANSLATE_RANGE_PX = RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX;
|
|
173
|
+
var RESTING_MOTION_DEFAULT_ROTATE_DEG = 5;
|
|
174
|
+
var RESTING_MOTION_PULSE_DIP_BASE = 0.38;
|
|
175
|
+
var restingMotionScalePatternDurationMs = (r) => {
|
|
176
|
+
const def = RESTING_MOTION_DEFAULT_DURATION_MS.scale;
|
|
177
|
+
if (r.preset !== "scale") return def;
|
|
178
|
+
if (r.loop !== true) {
|
|
179
|
+
return r.durationMs ?? r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;
|
|
180
|
+
}
|
|
181
|
+
return r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;
|
|
182
|
+
};
|
|
183
|
+
var restingMotionPhase01 = (cfg, localMs) => {
|
|
184
|
+
const segment = restingMotionEffectiveDurationMs(cfg);
|
|
185
|
+
const cycle = restingMotionCycleDurationMs(cfg);
|
|
186
|
+
if (segment <= 0) return 0;
|
|
187
|
+
if (cfg.preset === "scale") {
|
|
188
|
+
const pattern = restingMotionScalePatternDurationMs(cfg);
|
|
189
|
+
if (pattern <= 0) return 0;
|
|
190
|
+
if (cfg.loop === true) {
|
|
191
|
+
const u = (localMs % pattern + pattern) % pattern;
|
|
192
|
+
return u / pattern;
|
|
193
|
+
}
|
|
194
|
+
return Math.min(1, Math.max(0, localMs / pattern));
|
|
195
|
+
}
|
|
196
|
+
if (cfg.loop === true && cycle > 0) {
|
|
197
|
+
const u = (localMs % cycle + cycle) % cycle;
|
|
198
|
+
return u / cycle;
|
|
199
|
+
}
|
|
200
|
+
return Math.min(1, Math.max(0, localMs / segment));
|
|
201
|
+
};
|
|
202
|
+
var restingMotionScaleDirection = (r) => {
|
|
203
|
+
"worklet";
|
|
204
|
+
if (r.scaleDirection === "down") return "down";
|
|
205
|
+
if (r.scaleDirection === "up") return "up";
|
|
206
|
+
const down = r.scaleDownPercent ?? 0;
|
|
207
|
+
const up = r.scaleUpPercent ?? 0;
|
|
208
|
+
if (down > 0 && up === 0) return "down";
|
|
209
|
+
return "up";
|
|
210
|
+
};
|
|
211
|
+
var restingMotionScalePercentResolved = (r) => {
|
|
212
|
+
"worklet";
|
|
213
|
+
if (r.scalePercent !== void 0) return r.scalePercent;
|
|
214
|
+
if (restingMotionScaleDirection(r) === "down") {
|
|
215
|
+
return r.scaleDownPercent !== void 0 ? r.scaleDownPercent : r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT;
|
|
216
|
+
}
|
|
217
|
+
return r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;
|
|
218
|
+
};
|
|
219
|
+
var restingMotionScaleAmountFraction = (r) => {
|
|
220
|
+
"worklet";
|
|
221
|
+
return restingMotionScalePercentResolved(r) / 100 * restingMotionIntensity(r);
|
|
222
|
+
};
|
|
223
|
+
var restingMotionScaleSpringBack = (r) => {
|
|
224
|
+
"worklet";
|
|
225
|
+
return r.scaleSpringBack !== false;
|
|
226
|
+
};
|
|
227
|
+
var restingMotionScaleUpFraction = (r) => restingMotionScaleDirection(r) === "up" ? restingMotionScaleAmountFraction(r) : 0;
|
|
228
|
+
var restingMotionScaleDownFraction = (r) => restingMotionScaleDirection(r) === "down" ? restingMotionScaleAmountFraction(r) : 0;
|
|
229
|
+
var restingMotionScalePeakMultiplier = (cfg) => {
|
|
230
|
+
"worklet";
|
|
231
|
+
const amt = restingMotionScaleAmountFraction(cfg);
|
|
232
|
+
const dir = restingMotionScaleDirection(cfg);
|
|
233
|
+
return dir === "up" ? 1 + amt : 1 - amt;
|
|
234
|
+
};
|
|
235
|
+
var restingMotionScaleAtPhase = (cfg, ph) => {
|
|
236
|
+
"worklet";
|
|
237
|
+
const peak = restingMotionScalePeakMultiplier(cfg);
|
|
238
|
+
const p = Math.min(1, Math.max(0, ph));
|
|
239
|
+
if (!restingMotionScaleSpringBack(cfg)) {
|
|
240
|
+
return 1 + (peak - 1) * p;
|
|
241
|
+
}
|
|
242
|
+
if (p < 0.5) {
|
|
243
|
+
const t2 = p * 2;
|
|
244
|
+
return 1 + (peak - 1) * t2;
|
|
245
|
+
}
|
|
246
|
+
const t = (p - 0.5) * 2;
|
|
247
|
+
return peak + (1 - peak) * t;
|
|
248
|
+
};
|
|
249
|
+
var legacyTranslatePxToDisplayPercent = (px) => Math.max(-200, Math.min(200, Math.round(px / 80 * 200)));
|
|
250
|
+
var restingMotionTranslateBasePeak = (r) => {
|
|
251
|
+
"worklet";
|
|
252
|
+
const hasPercent = r.translatePeakXPercent !== void 0 || r.translatePeakYPercent !== void 0;
|
|
253
|
+
const hasPx = r.translatePeakXPx !== void 0 || r.translatePeakYPx !== void 0;
|
|
254
|
+
const hasLegacyRange = r.translateRangePx !== void 0;
|
|
255
|
+
if (hasPercent) {
|
|
256
|
+
return {
|
|
257
|
+
unit: "percent",
|
|
258
|
+
x: r.translatePeakXPercent ?? 0,
|
|
259
|
+
y: r.translatePeakYPercent ?? 0
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (hasPx || hasLegacyRange) {
|
|
263
|
+
if (hasPx) {
|
|
264
|
+
return {
|
|
265
|
+
unit: "px",
|
|
266
|
+
x: r.translatePeakXPx ?? 0,
|
|
267
|
+
y: r.translatePeakYPx ?? 0
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return { unit: "px", x: 0, y: r.translateRangePx ?? 0 };
|
|
271
|
+
}
|
|
272
|
+
return { unit: "percent", x: 0, y: RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT };
|
|
273
|
+
};
|
|
274
|
+
var restingMotionTranslateAuthoringPeakPercent = (r) => {
|
|
275
|
+
const base = restingMotionTranslateBasePeak(r);
|
|
276
|
+
if (base.unit === "percent") return { x: base.x, y: base.y };
|
|
277
|
+
return {
|
|
278
|
+
x: legacyTranslatePxToDisplayPercent(base.x),
|
|
279
|
+
y: legacyTranslatePxToDisplayPercent(base.y)
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
var restingMotionTranslateAuthoringPeakPx = (r) => {
|
|
283
|
+
const base = restingMotionTranslateBasePeak(r);
|
|
284
|
+
if (base.unit === "px") return { x: base.x, y: base.y };
|
|
285
|
+
return { x: 0, y: 0 };
|
|
286
|
+
};
|
|
287
|
+
var restingMotionTranslatePeakResolved = (r) => {
|
|
288
|
+
"worklet";
|
|
289
|
+
const base = restingMotionTranslateBasePeak(r);
|
|
290
|
+
const i = restingMotionIntensity(r);
|
|
291
|
+
if (base.unit === "percent") {
|
|
292
|
+
let x2 = base.x * i;
|
|
293
|
+
let y2 = base.y * i;
|
|
294
|
+
x2 = Math.max(-200, Math.min(200, x2));
|
|
295
|
+
y2 = Math.max(-200, Math.min(200, y2));
|
|
296
|
+
return { unit: "percent", x: x2, y: y2 };
|
|
297
|
+
}
|
|
298
|
+
let x = base.x * i;
|
|
299
|
+
let y = base.y * i;
|
|
300
|
+
x = Math.max(-200, Math.min(200, x));
|
|
301
|
+
y = Math.max(-200, Math.min(200, y));
|
|
302
|
+
return { unit: "px", x, y };
|
|
303
|
+
};
|
|
304
|
+
var restingMotionTranslatePeakPx = (r) => {
|
|
305
|
+
const r2 = restingMotionTranslatePeakResolved(r);
|
|
306
|
+
return { x: r2.x, y: r2.y };
|
|
307
|
+
};
|
|
308
|
+
var restingMotionTranslateSpringBack = (r) => {
|
|
309
|
+
"worklet";
|
|
310
|
+
return r.translateSpringBack !== false;
|
|
311
|
+
};
|
|
312
|
+
var restingMotionRotateMaxDeg = (r) => {
|
|
313
|
+
"worklet";
|
|
314
|
+
const raw = (r.rotateMaxDeg ?? RESTING_MOTION_DEFAULT_ROTATE_DEG) * restingMotionIntensity(r);
|
|
315
|
+
return Math.max(0, Math.min(360, raw));
|
|
316
|
+
};
|
|
317
|
+
var restingMotionRotateSpringBack = (r) => {
|
|
318
|
+
"worklet";
|
|
319
|
+
return r.rotateSpringBack !== false;
|
|
320
|
+
};
|
|
321
|
+
var restingMotionRotateDirection = (r) => {
|
|
322
|
+
"worklet";
|
|
323
|
+
return r.rotateDirection === "counterclockwise" ? "counterclockwise" : "clockwise";
|
|
324
|
+
};
|
|
325
|
+
var restingMotionRotateSign = (r) => {
|
|
326
|
+
"worklet";
|
|
327
|
+
return restingMotionRotateDirection(r) === "counterclockwise" ? -1 : 1;
|
|
328
|
+
};
|
|
329
|
+
var restingMotionPulseMinOpacity = (r) => {
|
|
330
|
+
"worklet";
|
|
331
|
+
if (r.pulseMinOpacity !== void 0) {
|
|
332
|
+
return Math.max(0, Math.min(1, r.pulseMinOpacity));
|
|
333
|
+
}
|
|
334
|
+
return Math.max(
|
|
335
|
+
0,
|
|
336
|
+
Math.min(1, 1 - RESTING_MOTION_PULSE_DIP_BASE * restingMotionIntensity(r))
|
|
337
|
+
);
|
|
338
|
+
};
|
|
339
|
+
var restingMotionSampleStyle = (cfg, ph) => {
|
|
340
|
+
switch (cfg.preset) {
|
|
341
|
+
case "translate": {
|
|
342
|
+
const peak = restingMotionTranslatePeakResolved(cfg);
|
|
343
|
+
const env = restingMotionTranslateSpringBack(cfg) ? Math.sin(ph * Math.PI) : ph;
|
|
344
|
+
let tx = env * peak.x;
|
|
345
|
+
let ty = env * peak.y;
|
|
346
|
+
if (Math.abs(tx) < 1e-6) tx = 0;
|
|
347
|
+
if (Math.abs(ty) < 1e-6) ty = 0;
|
|
348
|
+
if (peak.unit === "percent") {
|
|
349
|
+
return { transform: `translate(${tx}%, ${ty}%)`, willChange: "transform" };
|
|
350
|
+
}
|
|
351
|
+
return { transform: `translate(${tx}px, ${ty}px)`, willChange: "transform" };
|
|
352
|
+
}
|
|
353
|
+
case "bounce": {
|
|
354
|
+
const y = bouncePhaseToTranslateY(ph, restingMotionBounceAmplitudePx(cfg));
|
|
355
|
+
return { transform: `translateY(${y}px)`, willChange: "transform" };
|
|
356
|
+
}
|
|
357
|
+
case "scale": {
|
|
358
|
+
const sc = restingMotionScaleAtPhase(cfg, ph);
|
|
359
|
+
return { transform: `scale(${sc})`, willChange: "transform" };
|
|
360
|
+
}
|
|
361
|
+
case "pulse": {
|
|
362
|
+
const omin = restingMotionPulseMinOpacity(cfg);
|
|
363
|
+
const dip = 1 - omin;
|
|
364
|
+
const op = ph <= 0.5 ? 1 - ph * 2 * dip : 1 - (1 - ph) * 2 * dip;
|
|
365
|
+
return { opacity: op, willChange: "opacity" };
|
|
366
|
+
}
|
|
367
|
+
case "rotate": {
|
|
368
|
+
const peakDeg = restingMotionRotateMaxDeg(cfg);
|
|
369
|
+
let deg = restingMotionRotateSign(cfg) * (restingMotionRotateSpringBack(cfg) ? Math.sin(ph * Math.PI) * peakDeg : ph * peakDeg);
|
|
370
|
+
if (Math.abs(deg) < 1e-6) deg = 0;
|
|
371
|
+
return { transform: `rotate(${deg}deg)`, willChange: "transform" };
|
|
372
|
+
}
|
|
373
|
+
default:
|
|
374
|
+
return {};
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
var restingMotionStyleAtTime = (screen, layerId, cfg, tMs) => {
|
|
378
|
+
const local = restingMotionLocalElapsedMs(screen, layerId, cfg, tMs);
|
|
379
|
+
if (local === null) return null;
|
|
380
|
+
const ph = restingMotionPhase01(cfg, local);
|
|
381
|
+
return restingMotionSampleStyle(cfg, ph);
|
|
382
|
+
};
|
|
383
|
+
var RESTING_MOTION_KEYFRAMES_CSS = `
|
|
384
|
+
@keyframes ob-rm-translate {
|
|
385
|
+
0%, 100% { transform: translate(0, 0); }
|
|
386
|
+
50% {
|
|
387
|
+
transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
@keyframes ob-rm-translate-ramp {
|
|
391
|
+
0% { transform: translate(0, 0); }
|
|
392
|
+
100% {
|
|
393
|
+
transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
@keyframes ob-rm-bounce {
|
|
397
|
+
0%, 100% { transform: translateY(0); }
|
|
398
|
+
50% { transform: translateY(calc(-1 * var(--ob-rm-bounce-px, 14px))); }
|
|
399
|
+
}
|
|
400
|
+
@keyframes ob-rm-scale {
|
|
401
|
+
0%, 100% { transform: scale(1); }
|
|
402
|
+
50% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }
|
|
403
|
+
}
|
|
404
|
+
@keyframes ob-rm-scale-ramp {
|
|
405
|
+
0% { transform: scale(1); }
|
|
406
|
+
100% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }
|
|
407
|
+
}
|
|
408
|
+
@keyframes ob-rm-pulse {
|
|
409
|
+
0%, 100% { opacity: 1; }
|
|
410
|
+
50% { opacity: var(--ob-rm-pulse-min, 0.62); }
|
|
411
|
+
}
|
|
412
|
+
@keyframes ob-rm-rotate {
|
|
413
|
+
0%, 100% { transform: rotate(0deg); }
|
|
414
|
+
50% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }
|
|
415
|
+
}
|
|
416
|
+
@keyframes ob-rm-rotate-ramp {
|
|
417
|
+
0% { transform: rotate(0deg); }
|
|
418
|
+
100% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }
|
|
419
|
+
}
|
|
420
|
+
`.trim();
|
|
421
|
+
var restingMotionWebAnimationName = (config) => {
|
|
422
|
+
if (config.preset === "rotate" && !restingMotionRotateSpringBack(config)) {
|
|
423
|
+
return "ob-rm-rotate-ramp";
|
|
424
|
+
}
|
|
425
|
+
if (config.preset === "translate" && !restingMotionTranslateSpringBack(config)) {
|
|
426
|
+
return "ob-rm-translate-ramp";
|
|
427
|
+
}
|
|
428
|
+
if (config.preset === "scale" && !restingMotionScaleSpringBack(config)) {
|
|
429
|
+
return "ob-rm-scale-ramp";
|
|
430
|
+
}
|
|
431
|
+
return `ob-rm-${config.preset}`;
|
|
432
|
+
};
|
|
433
|
+
var restingMotionWebStyle = (config) => {
|
|
434
|
+
const duration = restingMotionEffectiveDurationMs(config);
|
|
435
|
+
const intensity = restingMotionIntensity(config);
|
|
436
|
+
const name = restingMotionWebAnimationName(config);
|
|
437
|
+
const oneCycleMs = config.preset === "scale" ? restingMotionScalePatternDurationMs(config) : config.loop === true ? restingMotionCycleDurationMs(config) : duration;
|
|
438
|
+
const iter = config.loop === true ? "infinite" : "1 forwards";
|
|
439
|
+
const base = {
|
|
440
|
+
["--ob-rm-i"]: String(intensity),
|
|
441
|
+
animation: `${name} ${oneCycleMs}ms ease-in-out ${iter}`,
|
|
442
|
+
willChange: config.preset === "pulse" ? "opacity" : "transform"
|
|
443
|
+
};
|
|
444
|
+
if (config.preset === "bounce") {
|
|
445
|
+
return {
|
|
446
|
+
...base,
|
|
447
|
+
["--ob-rm-bounce-px"]: `${restingMotionBounceAmplitudePx(config)}px`
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (config.preset === "scale") {
|
|
451
|
+
return {
|
|
452
|
+
...base,
|
|
453
|
+
["--ob-rm-scale-peak"]: String(restingMotionScalePeakMultiplier(config))
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (config.preset === "translate") {
|
|
457
|
+
const peak = restingMotionTranslatePeakResolved(config);
|
|
458
|
+
const sx = peak.unit === "percent" ? `${peak.x}%` : `${peak.x}px`;
|
|
459
|
+
const sy = peak.unit === "percent" ? `${peak.y}%` : `${peak.y}px`;
|
|
460
|
+
return {
|
|
461
|
+
...base,
|
|
462
|
+
["--ob-rm-translate-peak-x"]: sx,
|
|
463
|
+
["--ob-rm-translate-peak-y"]: sy
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
if (config.preset === "rotate") {
|
|
467
|
+
const signedPeak = restingMotionRotateSign(config) * restingMotionRotateMaxDeg(config);
|
|
468
|
+
return {
|
|
469
|
+
...base,
|
|
470
|
+
["--ob-rm-rotate-peak"]: `${signedPeak}deg`
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (config.preset === "pulse") {
|
|
474
|
+
return {
|
|
475
|
+
...base,
|
|
476
|
+
["--ob-rm-pulse-min"]: String(restingMotionPulseMinOpacity(config))
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
return base;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
export { DEFAULT_WORKBENCH_TIMELINE_MS, LEGACY_RESTING_MOTION_ID, 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, bouncePhaseToTranslateY, clampRestingMotionSegmentDurationMs, layerMountClipsEndMs, layerRestingMotionEndMs, layerRestingMotionEntries, layerRestingMotionStartMs, layerWithRestingMotionEntries, motionTimelineScrubClampMs, 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, screenRestingTimelineExtentMs, workbenchTimelineTotalMs };
|
|
483
|
+
//# sourceMappingURL=restingMotion.js.map
|
|
484
|
+
//# sourceMappingURL=restingMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/restingMotion/restingMotionEntries.ts","../src/layers.ts","../src/animations.ts","../src/restingMotion/restingMotionTimeline.ts","../src/restingMotion/restingMotionEffects.ts"],"names":["t","x","y"],"mappings":"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","file":"restingMotion.js","sourcesContent":["import type { RestingMotion, RestingMotionEntry, RestingMotionPreset } from '@getrheo/contracts/layers';\n\n\nexport type RuntimeStyle = Record<string, string | number | undefined>;\n\nexport const RESTING_MOTION_DEFAULT_DURATION_MS: Record<RestingMotionPreset, number> = {\n translate: 2400,\n bounce: 2000,\n scale: 2200,\n pulse: 1800,\n rotate: 3200,\n};\n\n/** Matches {@link RestingMotionSchema} `durationMs` / `scalePatternDurationMs` bounds. */\nexport const RESTING_MOTION_SEGMENT_DURATION_MS_MIN = 200;\nexport const RESTING_MOTION_SEGMENT_DURATION_MS_MAX = 20_000;\n\nexport const clampRestingMotionSegmentDurationMs = (ms: number): number =>\n Math.min(\n RESTING_MOTION_SEGMENT_DURATION_MS_MAX,\n Math.max(RESTING_MOTION_SEGMENT_DURATION_MS_MIN, Math.round(ms)),\n );\n\n/** Synthetic id for manifests that only had {@link Layer.restingMotion} (no `restingMotions`). */\nexport const LEGACY_RESTING_MOTION_ID = '__legacy' as const;\n\n/**\n * Canonical list of motion segments for a layer (array wins; legacy single field becomes one entry).\n */\nexport const layerRestingMotionEntries = (layer: {\n restingMotion?: RestingMotion;\n restingMotions?: RestingMotionEntry[] | null;\n}): RestingMotionEntry[] => {\n const list = layer.restingMotions;\n if (list && list.length > 0) return list;\n if (layer.restingMotion) {\n return [{ ...layer.restingMotion, id: LEGACY_RESTING_MOTION_ID }];\n }\n return [];\n};\n\n/** Persist motion segments; clears legacy single field when using the array. */\nexport const layerWithRestingMotionEntries = <\n L extends { restingMotion?: RestingMotion; restingMotions?: RestingMotionEntry[] },\n>(\n layer: L,\n entries: RestingMotionEntry[],\n): L => {\n if (entries.length === 0) {\n const next = { ...layer } as Record<string, unknown>;\n delete next.restingMotion;\n delete next.restingMotions;\n return next as L;\n }\n return {\n ...layer,\n restingMotions: entries.map((e) => ({ ...e })),\n restingMotion: undefined,\n };\n};\n\n/**\n * Non-loop scale: keep timeline segment and pattern duration identical in saved state.\n * Call when updating motion speed or clip length from the builder.\n */\nexport const restingMotionSyncScaleNonLoopClipAndPattern = (\n r: RestingMotion,\n ms: number,\n): RestingMotion => {\n const v = clampRestingMotionSegmentDurationMs(ms);\n return { ...r, durationMs: v, scalePatternDurationMs: v };\n};\n\n/** Align stored fields after paste/import when scale is one-shot. */\nexport const restingMotionNormalizeScaleNonLoopClip = (r: RestingMotion): RestingMotion => {\n if (r.preset !== 'scale' || r.loop === true) return r;\n const ms = clampRestingMotionSegmentDurationMs(\n r.durationMs ?? r.scalePatternDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS.scale,\n );\n return { ...r, durationMs: ms, scalePatternDurationMs: ms };\n};\n\n/** Timeline segment length: motion runs from start until start + this value. */\nexport const restingMotionEffectiveDurationMs = (r: RestingMotion): number =>\n r.durationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];\n\n/** One full pattern cycle (ms), used when looping. */\nexport const restingMotionCycleDurationMs = (r: RestingMotion): number =>\n r.cycleDurationMs ?? RESTING_MOTION_DEFAULT_DURATION_MS[r.preset];\n\n/**\n * Value for the Motion speed slider: one-shot → clip length ({@link restingMotionEffectiveDurationMs});\n * looping → one repeat ({@link restingMotionCycleDurationMs}).\n */\nexport const restingMotionMotionSpeedSliderMs = (r: RestingMotion): number =>\n r.loop === true ? restingMotionCycleDurationMs(r) : restingMotionEffectiveDurationMs(r);\n\nexport const restingMotionIntensity = (r: RestingMotion): number => {\n 'worklet';\n return r.intensity ?? 1;\n};\n\nexport const restingMotionDelayAfterMountEndMs = (r: RestingMotion): number =>\n r.delayMsAfterMountEnd ?? 0;\n","import type { Branding } from '@getrheo/contracts/dashboard';\nimport type { FlowManifest, Theme } from '@getrheo/contracts/manifest';\nimport type { Screen } from '@getrheo/contracts/screens';\nimport {\n brandGradientFromThemedColor,\n brandGradientNativeLinear,\n brandGradientSolidFallback,\n isStoredLinearGradientCss,\n nativeLinearFromAngleAndStops,\n parseLinearGradientCss,\n resolveBrandGradientToken,\n type BrandGradientNativeLinear,\n} from './brandGradient';\nimport type {\n InputLayer,\n Layer,\n MultipleChoiceLayer,\n ScaleInputLayer,\n SingleChoiceLayer,\n StackLayer,\n TextInputLayer,\n ThemedColor,\n} from '@getrheo/contracts/layers';\nimport type { LocalizedText } from '@getrheo/contracts/localized';\nimport { resolveLocalizedText } from '@getrheo/contracts/localized';\nimport { isInputLayer } from '@getrheo/contracts/layers';\n\n/** Walk a layer tree depth-first. */\nexport const walkLayers = (root: Layer, fn: (l: Layer, depth: number) => void): void => {\n const visit = (l: Layer, depth: number): void => {\n fn(l, depth);\n if (l.kind === 'stack') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'carousel') l.slides.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'button') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'back_button') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'hyperlink') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'single_choice' || l.kind === 'multiple_choice') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'text_input' || l.kind === 'scale_input') {\n l.children?.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'oauth_login') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'oauth_provider' && l.variant === 'custom') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_auth') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_field') {\n l.children?.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_submit') {\n l.children.forEach((c) => visit(c, depth + 1));\n }\n };\n visit(root, 0);\n};\n\n/** Walk every layer in a screen's regions (header → body → footer). */\nexport const walkScreen = (screen: Screen, fn: (l: Layer) => void): void => {\n if (screen.regions.header) walkLayers(screen.regions.header, fn);\n walkLayers(screen.regions.body, fn);\n if (screen.regions.footer) walkLayers(screen.regions.footer, fn);\n};\n\n/** Find the screen's lone input layer (if any). Schema enforces ≤1. */\nexport const findInputLayer = (screen: Screen): InputLayer | null => {\n let found: InputLayer | null = null;\n walkScreen(screen, (l) => {\n if (!found && isInputLayer(l)) found = l;\n });\n return found;\n};\n\n/** Input kinds that use a screen draft and require an explicit Continue to submit. */\nexport const findManualSubmitInputLayer = (\n screen: Screen,\n): MultipleChoiceLayer | TextInputLayer | ScaleInputLayer | null => {\n const input = findInputLayer(screen);\n if (!input) return null;\n if (input.kind === 'multiple_choice' || input.kind === 'text_input' || input.kind === 'scale_input') {\n return input;\n }\n return null;\n};\n\n/**\n * Whether the screen contains any Button layer that submits the screen\n * (i.e. `action.kind === 'continue'`). Used by input layers to decide\n * between auto-submit-on-tap (legacy behaviour for choice-only screens)\n * and writing into the screen-level draft for a Button to submit.\n */\nexport const screenHasContinueButton = (screen: Screen): boolean => {\n let found = false;\n walkScreen(screen, (l) => {\n if (l.kind === 'button' && l.action.kind === 'continue') found = true;\n });\n return found;\n};\n\n/**\n * Resolve a choice layer's option stack by stable optionId via its\n * binding. Returns null when the binding is missing or the bound child\n * was removed (manifest validation rejects that, but runtime stays safe).\n */\nexport const findOptionStackForChoice = (\n layer: SingleChoiceLayer | MultipleChoiceLayer,\n optionId: string,\n): StackLayer | null => {\n const binding = layer.optionBindings.find((b) => b.optionId === optionId);\n if (!binding) return null;\n const stack = layer.children.find((c) => c.id === binding.rootLayerId);\n return stack ?? null;\n};\n\n/**\n * Best-effort textual label for a choice option, used by interpolation\n * (e.g. `{{ goal }}` rendering the chosen option's label) and by editor\n * surfaces that show option rows. Walks the option's child subtree\n * depth-first and returns the first `text` layer's content; falls back\n * to the option's stable id when no text is present.\n */\nexport const choiceOptionLabel = (\n layer: SingleChoiceLayer | MultipleChoiceLayer,\n optionId: string,\n locale: string,\n): string => {\n const stack = findOptionStackForChoice(layer, optionId);\n if (!stack) return '';\n let foundText: LocalizedText | null = null;\n walkLayers(stack, (l) => {\n if (foundText) return;\n if (l.kind === 'text') foundText = l.text;\n });\n if (foundText) return resolveLocalizedText(foundText, locale);\n return optionId;\n};\n\n/** Find a layer in a screen by id, including nested children/slides. */\nexport const findLayerById = (screen: Screen, id: string): Layer | null => {\n let found: Layer | null = null;\n walkScreen(screen, (l) => {\n if (!found && l.id === id) found = l;\n });\n return found;\n};\n\n/** Collect all input fieldKeys across a manifest. */\nexport const collectFieldKeys = (manifest: FlowManifest): { fieldKey: string; screenId: string }[] => {\n const out: { fieldKey: string; screenId: string }[] = [];\n for (const screen of manifest.screens) {\n walkScreen(screen as unknown as Screen, (l) => {\n if (isInputLayer(l)) out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n if (l.kind === 'checkbox') out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n if (l.kind === 'email_password_auth') {\n out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n }\n });\n }\n return out;\n};\n\n/**\n * Pick a snake_case field key starting from `base` that is not in `used`\n * (e.g. `text` → `text_2` → `text_3` when `text` is taken).\n */\nexport const nextUniqueFieldKey = (base: string, used: Iterable<string>): string => {\n const set = used instanceof Set ? used : new Set(used);\n if (!set.has(base)) return base;\n let n = 2;\n while (set.has(`${base}_${n}`)) n += 1;\n return `${base}_${n}`;\n};\n\n/**\n * Resolve a token reference like `$primary` to a literal value from `theme`.\n * Pass-through for non-token strings; returns undefined for `undefined`.\n */\nexport const resolveTokens = <T extends string | undefined>(\n theme: Theme | undefined,\n value: T,\n): T | string => {\n if (value === undefined) return value;\n if (typeof value !== 'string' || !value.startsWith('$')) return value;\n const key = value.slice(1) as keyof Theme;\n const literal = theme?.[key];\n if (typeof literal === 'string') return literal;\n return value;\n};\n\n/**\n * Resolve a layer color for the current appearance (`light` | `dark`).\n * Plain string uses `resolveTokens` for both modes (legacy). Object form\n * picks `light` / `dark` with fallback to the other key when one is omitted.\n */\nexport const resolveThemedColor = (\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): string | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === 'string') return resolveTokens(theme, value) as string;\n const raw = palette === 'dark' ? (value.dark ?? value.light) : (value.light ?? value.dark);\n if (raw === undefined) return undefined;\n return resolveTokens(theme, raw) as string;\n};\n\n/**\n * Resolve a themed value used for CSS `background` (or RN background fill).\n * Supports `$brandGradient:<uuid>` when branding presets are provided; other values match {@link resolveThemedColor}.\n */\nexport const resolveThemedBackground = (\n theme: Theme | undefined,\n branding: Branding | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): string | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === 'string') {\n if (value.startsWith('$brandGradient:')) {\n return resolveBrandGradientToken(branding, value);\n }\n return resolveTokens(theme, value) as string;\n }\n const raw = palette === 'dark' ? (value.dark ?? value.light) : (value.light ?? value.dark);\n if (raw === undefined) return undefined;\n if (raw.startsWith('$brandGradient:')) {\n return resolveBrandGradientToken(branding, raw);\n }\n return resolveTokens(theme, raw) as string;\n};\n\nexport const nativeBrandBackgroundFromThemedColor = (\n theme: Theme | undefined,\n branding: Branding | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): { solid?: string; linear?: BrandGradientNativeLinear } => {\n const preset = brandGradientFromThemedColor(branding, palette, value);\n if (preset) {\n const lin = brandGradientNativeLinear(preset);\n if (lin) return { linear: lin };\n return { solid: brandGradientSolidFallback(preset) };\n }\n const bg = resolveThemedBackground(theme, branding, palette, value) as string | undefined;\n if (!bg) return {};\n const parsed = parseLinearGradientCss(bg);\n if (parsed) {\n return {\n linear: nativeLinearFromAngleAndStops(\n parsed.angleDeg,\n parsed.stops.map((s) => ({ color: s.color, offsetPct: s.offsetPct })),\n ),\n };\n }\n if (isStoredLinearGradientCss(bg)) {\n const first = bg.match(/#[0-9a-fA-F]{3,8}/);\n return { solid: first ? first[0] : '#808080' };\n }\n return { solid: bg };\n};\n","import {\n type AnimatableProperty,\n type AnimationClip,\n type EasingToken,\n type Keyframe,\n type KeyframeTrack,\n} from '@getrheo/contracts/animations';\nimport type { Layer, LayerKind, StackLayer } from '@getrheo/contracts/layers';\nimport type { Screen } from '@getrheo/contracts/screens';\nimport { walkScreen } from './layers';\n\n/**\n * Cubic-bezier control points for every named easing token. The exact\n * numbers are shared between web (CSS keyframes / `cubic-bezier(...)`)\n * and native (Reanimated `Easing.bezier`) so playback matches sample by\n * sample.\n */\nexport const EASING_BEZIERS: Record<EasingToken, [number, number, number, number]> = {\n linear: [0, 0, 1, 1],\n 'ease-in': [0.42, 0, 1, 1],\n 'ease-out': [0, 0, 0.58, 1],\n 'ease-in-out': [0.42, 0, 0.58, 1],\n // Material-style \"standard\" / \"emphasized\" curves — useful defaults\n // for product animations without inviting per-platform divergence.\n standard: [0.2, 0, 0, 1],\n emphasized: [0.3, 0, 0, 1],\n};\n\nconst sampleBezier = (\n t: number,\n [x1, y1, x2, y2]: [number, number, number, number],\n): number => {\n // Robust numeric solver for the parametric cubic-bezier curve. We need\n // to find the parameter `s` such that `bezierX(s) === t`, then return\n // `bezierY(s)`. Newton-Raphson with a fallback bisection keeps us\n // accurate at the curve endpoints (where derivatives near zero make\n // pure Newton flaky).\n if (x1 === y1 && x2 === y2) return t;\n const ax = 3 * x1 - 3 * x2 + 1;\n const bx = 3 * x2 - 6 * x1;\n const cx = 3 * x1;\n const ay = 3 * y1 - 3 * y2 + 1;\n const by = 3 * y2 - 6 * y1;\n const cy = 3 * y1;\n const xAt = (s: number) => ((ax * s + bx) * s + cx) * s;\n const yAt = (s: number) => ((ay * s + by) * s + cy) * s;\n const dxAt = (s: number) => (3 * ax * s + 2 * bx) * s + cx;\n let s = t;\n for (let i = 0; i < 8; i++) {\n const x = xAt(s) - t;\n const dx = dxAt(s);\n if (Math.abs(x) < 1e-6) return yAt(s);\n if (Math.abs(dx) < 1e-6) break;\n s -= x / dx;\n }\n let lo = 0;\n let hi = 1;\n s = t;\n for (let i = 0; i < 24; i++) {\n const x = xAt(s);\n if (Math.abs(x - t) < 1e-6) return yAt(s);\n if (x < t) lo = s;\n else hi = s;\n s = (lo + hi) / 2;\n }\n return yAt(s);\n};\n\n/**\n * Map an authoring time `tNorm ∈ [0,1]` to the eased value for a single\n * track. Returns the final value when t ≥ last keyframe and the first\n * value when t ≤ first keyframe so renderers can hold pre/post values\n * without special-casing edges.\n */\nexport const sampleTrack = (track: KeyframeTrack, tNorm: number): number => {\n const ks = track.keyframes;\n if (tNorm <= ks[0]!.t) return ks[0]!.value;\n if (tNorm >= ks[ks.length - 1]!.t) return ks[ks.length - 1]!.value;\n let prev: Keyframe = ks[0]!;\n let next: Keyframe = ks[ks.length - 1]!;\n for (let i = 0; i < ks.length - 1; i++) {\n if (ks[i]!.t <= tNorm && ks[i + 1]!.t >= tNorm) {\n prev = ks[i]!;\n next = ks[i + 1]!;\n break;\n }\n }\n const span = next.t - prev.t;\n const localT = span === 0 ? 0 : (tNorm - prev.t) / span;\n const eased = sampleBezier(localT, EASING_BEZIERS[prev.easing ?? 'linear']);\n return prev.value + (next.value - prev.value) * eased;\n};\n\nexport type SampledClip = Partial<Record<AnimatableProperty, number>>;\n\n/** Effective delay for a clip given its owning screen's stagger config. */\nexport const effectiveDelayMs = (clip: AnimationClip, screen: Screen): number => {\n const base = clip.delayMs ?? 0;\n if (clip.trigger === 'stagger' && clip.staggerIndex !== undefined) {\n const step = screen.stagger?.stepMs ?? 60;\n return base + clip.staggerIndex * step;\n }\n return base;\n};\n\n/**\n * Sample every track of a clip at an absolute time `tMs` measured from\n * the clip's mount. Honors `delayMs` (incl. stagger). Always returns the\n * authored property keys, even if the clip has not yet started, so\n * renderers can treat the result as a stable style overlay.\n */\nexport const sampleClipAt = (\n clip: AnimationClip,\n screen: Screen,\n tMs: number,\n): SampledClip => {\n const delay = effectiveDelayMs(clip, screen);\n const local = clip.durationMs > 0 ? (tMs - delay) / clip.durationMs : 1;\n const tNorm = Math.min(1, Math.max(0, local));\n const out: SampledClip = {};\n for (const track of clip.tracks) {\n out[track.property] = sampleTrack(track, tNorm);\n }\n return out;\n};\n\n/** Merge sampled clips: later keys override; only defined keys from `overlay` win. */\nexport const mergeSampledClips = (base: SampledClip, overlay: SampledClip): SampledClip => {\n const out: SampledClip = { ...base };\n for (const k of Object.keys(overlay) as (keyof SampledClip)[]) {\n const v = overlay[k];\n if (v !== undefined) out[k] = v;\n }\n return out;\n};\n\n/**\n * Sample all clips for `layerId` at global screen time `tMs`.\n *\n * Clips are applied in order of increasing start time (`effectiveDelayMs`),\n * then stable `id`. Each clip contributes once its start time has been\n * reached; after it ends, its **final** sampled values stay merged into\n * state so later clips override animating properties. For **mount** and\n * **stagger** clips, while `tMs` is before that clip's delay, we merge its\n * **start** keyframe values unless some **earlier** clip is inside its own\n * `[delay, delay + duration)` window — so a delayed fade-in stays at opacity\n * 0 during the wait, but a move that overlaps the fade's delay still shows\n * default opacity until the fade segment begins. **Unmount** clips never\n * apply pre-delay sampling. This supports multiple appear / hide segments on\n * the same layer (web sim / builder scrub).\n *\n * **Native:** `LayerMotionShell` still uses the first mount + first unmount\n * clip only until RN playback is extended for arbitrary sequences.\n */\nexport const sampleLayerAnimAt = (screen: Screen, layerId: string, tMs: number): SampledClip => {\n const list = clipsByLayerId(screen).get(layerId) ?? [];\n if (list.length === 0) return {};\n\n const sorted = [...list].sort((a, b) => {\n const da = effectiveDelayMs(a, screen);\n const db = effectiveDelayMs(b, screen);\n if (da !== db) return da - db;\n return a.id.localeCompare(b.id);\n });\n\n let state: SampledClip = {};\n for (let i = 0; i < sorted.length; i++) {\n const clip = sorted[i]!;\n const d = effectiveDelayMs(clip, screen);\n const end = d + clip.durationMs;\n if (tMs < d) {\n if (clip.trigger === 'unmount') continue;\n if (clip.trigger !== 'mount' && clip.trigger !== 'stagger') continue;\n const earlierAnimating = sorted.slice(0, i).some((earlier) => {\n const dE = effectiveDelayMs(earlier, screen);\n const endE = dE + earlier.durationMs;\n return tMs >= dE && tMs < endE;\n });\n if (earlierAnimating) continue;\n const pending = sampleClipAt(clip, screen, d);\n state = mergeSampledClips(state, pending);\n continue;\n }\n const at = tMs <= end ? tMs : end;\n state = mergeSampledClips(state, sampleClipAt(clip, screen, at));\n }\n return state;\n};\n\n/** True if this layer has any animation clip (mount, unmount, or legacy stagger). */\nexport const layerHasAnimationClips = (screen: Screen | undefined, layerId: string): boolean =>\n (screen?.animations ?? []).some((c) => c.targetLayerId === layerId);\n\n/** Total time (ms) before the screen's animations are visually settled. */\nexport const screenAnimationsDurationMs = (screen: Screen): number => {\n if (!screen.animations || screen.animations.length === 0) return 0;\n let max = 0;\n for (const clip of screen.animations) {\n const total = effectiveDelayMs(clip, screen) + clip.durationMs;\n if (total > max) max = total;\n }\n return max;\n};\n\n/** End of the loader fill segment on the timeline (ms from screen mount), for one loader layer. */\nexport const loaderLayerFillTimelineEndMs = (layer: Extract<Layer, { kind: 'loader' }>): number =>\n (layer.fillDelayMs ?? 0) + (layer.durationMs ?? 2000);\n\n/** Latest end time (ms from screen mount) of any loader timed fill on this screen. */\nexport const screenLoaderTimelineExtentMs = (screen: Screen): number => {\n let max = 0;\n walkScreen(screen, (l) => {\n if (l.kind !== 'loader') return;\n const end = loaderLayerFillTimelineEndMs(l);\n if (end > max) max = end;\n });\n return max;\n};\n\n/**\n * Fill progress 0–1 for scrubber / motion clock time `tMs` (linear; no easing).\n */\nexport const loaderFillProgressAtGlobalMs = (\n tMs: number,\n fillDelayMs: number,\n durationMs: number,\n): number => {\n if (durationMs <= 0) return tMs >= fillDelayMs ? 1 : 0;\n return Math.min(1, Math.max(0, (tMs - fillDelayMs) / durationMs));\n};\n\n/** Default timeline play span when a Lottie layer has no {@link durationMs}. */\nexport const DEFAULT_LOTTIE_PLAY_DURATION_MS = 2_000;\n\ntype LottieJsonMeta = { fr?: number; ip?: number; op?: number };\n\n/** Duration (ms) from Lottie JSON `ip` / `op` / `fr` when present. */\nexport const lottieJsonDurationMs = (animationData: object): number => {\n const j = animationData as LottieJsonMeta;\n const fr = typeof j.fr === 'number' && j.fr > 0 ? j.fr : 60;\n const ip = typeof j.ip === 'number' ? j.ip : 0;\n const op = typeof j.op === 'number' ? j.op : 60;\n const frames = Math.max(0, op - ip);\n return Math.max(1, Math.round((frames / fr) * 1000));\n};\n\n/**\n * @deprecated Prefer {@link workbenchTimelineTotalMs} from `@getrheo/flow-runtime/restingMotion`.\n * Kept for tests; returns mount/unmount clip extent only (no 10s floor).\n */\nexport const MIN_ANIMATION_TIMELINE_AUTHORING_SPAN_MS = 10_000;\n\n/** Mount/unmount clip extent only — does not include loader/Lottie/video/resting spans. */\nexport const animationTimelineAuthoringEndMs = (screen: Screen): number =>\n screenAnimationsDurationMs(screen);\n\n/**\n * Convert legacy `stagger` clips to `mount` with combined delay and drop\n * `screen.stagger` so authoring uses a single delay per clip.\n */\nexport const migrateStaggerClipsToMount = (screen: Screen): Screen => {\n const staggerClipPresent = screen.animations?.some((c) => c.trigger === 'stagger');\n const staggerFieldPresent = screen.stagger !== undefined;\n if (!staggerClipPresent && !staggerFieldPresent) return screen;\n\n const step = screen.stagger?.stepMs ?? 60;\n const { stagger: _st, ...rest } = screen;\n\n if (!staggerClipPresent) {\n return { ...rest };\n }\n\n const nextAnimations = (screen.animations ?? []).map((c) => {\n if (c.trigger !== 'stagger') return c;\n const base = c.delayMs ?? 0;\n const extra = (c.staggerIndex ?? 0) * step;\n return {\n ...c,\n trigger: 'mount' as const,\n delayMs: base + extra,\n staggerIndex: undefined,\n };\n });\n\n return { ...rest, animations: nextAnimations };\n};\n\nconst hasStaggerClip = (screen: Screen, layerId: string): boolean =>\n (screen.animations ?? []).some((c) => c.targetLayerId === layerId && c.trigger === 'stagger');\n\n/**\n * Reassigns `staggerIndex` on every stagger clip from **direct child order**\n * under each {@link StackLayer}. Matches how authors think about sibling\n * sequencing in the layer tree.\n */\nexport const applyStaggerIndicesFromTreeOrder = (screen: Screen): Screen => {\n if (!screen.animations?.some((c) => c.trigger === 'stagger')) return screen;\n\n const staggerIndexByLayerId = new Map<string, number>();\n\n const visitStackChildren = (stack: StackLayer): void => {\n let idx = 0;\n for (const ch of stack.children) {\n if (hasStaggerClip(screen, ch.id)) {\n staggerIndexByLayerId.set(ch.id, idx);\n idx += 1;\n }\n visit(ch);\n }\n };\n\n const visit = (l: Layer): void => {\n if (l.kind === 'stack') {\n visitStackChildren(l);\n return;\n }\n if (l.kind === 'carousel') {\n for (const s of l.slides) visit(s);\n return;\n }\n if (l.kind === 'button' || l.kind === 'back_button') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'hyperlink') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'single_choice' || l.kind === 'multiple_choice') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'text_input' || l.kind === 'scale_input') {\n l.children?.forEach(visit);\n return;\n }\n if (l.kind === 'oauth_login') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'oauth_provider' && l.variant === 'custom') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'email_password_auth') {\n l.children.forEach(visit);\n return;\n }\n if (l.kind === 'email_password_field') {\n l.children?.forEach(visit);\n return;\n }\n if (l.kind === 'email_password_submit') {\n l.children.forEach(visit);\n return;\n }\n };\n\n if (screen.regions.header) visit(screen.regions.header);\n visit(screen.regions.body);\n if (screen.regions.footer) visit(screen.regions.footer);\n\n const nextAnimations = screen.animations.map((c) => {\n if (c.trigger !== 'stagger') return c;\n const nextIdx = staggerIndexByLayerId.get(c.targetLayerId);\n if (nextIdx === undefined) return c;\n if (c.staggerIndex === nextIdx) return c;\n return { ...c, staggerIndex: nextIdx };\n });\n\n if (nextAnimations.every((c, i) => c === screen.animations![i])) return screen;\n return { ...screen, animations: nextAnimations };\n};\n\n/**\n * Index clips by `targetLayerId` so renderers don't re-walk the full\n * animations array per layer per frame.\n */\nexport const clipsByLayerId = (screen: Screen): Map<string, AnimationClip[]> => {\n const map = new Map<string, AnimationClip[]>();\n if (!screen.animations) return map;\n for (const clip of screen.animations) {\n const list = map.get(clip.targetLayerId) ?? [];\n list.push(clip);\n map.set(clip.targetLayerId, list);\n }\n return map;\n};\n\n/**\n * Reduced-motion policy used by every runtime. The default behavior is\n * conservative: animations snap to their final keyframe (no movement),\n * and screen transitions become instantaneous. Authors who want more\n * latitude can extend this in the future without diverging the\n * renderers.\n */\nexport type ReducedMotionPolicy = 'play' | 'snap-to-end';\n\nexport const applyReducedMotion = (\n clip: AnimationClip,\n policy: ReducedMotionPolicy,\n): AnimationClip => {\n if (policy === 'play') return clip;\n return {\n ...clip,\n durationMs: 0,\n delayMs: 0,\n tracks: clip.tracks.map((t) => ({\n ...t,\n keyframes: t.keyframes.map((k) => ({ ...k, t: k.t === 0 ? 0 : 1 })),\n })),\n };\n};\n\n/**\n * Whitelist of animatable properties per layer kind. Keep this in sync\n * with renderer support so the editor can disable unsupported tracks\n * before they hit the manifest. Today every layer supports the same\n * style-token subset; we keep the function shape so future layer kinds\n * can opt-in / opt-out without schema changes.\n */\nexport const listAnimatablePropsForLayerKind = (\n _kind: LayerKind,\n): readonly AnimatableProperty[] => ['opacity', 'translateX', 'translateY', 'scale'];\n\nexport const isPropertyAllowedOnLayer = (\n layer: Layer,\n property: AnimatableProperty,\n): boolean => listAnimatablePropsForLayerKind(layer.kind).includes(property);\n","import type { RestingMotion } from '@getrheo/contracts/layers';\nimport type { Screen } from '@getrheo/contracts/screens';\n\nimport {\n effectiveDelayMs,\n screenAnimationsDurationMs,\n screenLoaderTimelineExtentMs,\n} from '../animations.js';\nimport { walkScreen } from '../layers.js';\nimport {\n layerRestingMotionEntries,\n restingMotionDelayAfterMountEndMs,\n restingMotionEffectiveDurationMs,\n} from './restingMotionEntries.js';\n\n/**\n * Latest end time (ms from screen mount) of mount/stagger clips for a layer.\n * `0` when none — layer motion may run immediately.\n */\nexport const layerMountClipsEndMs = (screen: Screen, layerId: string): number => {\n const clips =\n screen.animations?.filter(\n (c) => c.targetLayerId === layerId && (c.trigger === 'mount' || c.trigger === 'stagger'),\n ) ?? [];\n let maxEnd = 0;\n for (const c of clips) {\n maxEnd = Math.max(maxEnd, effectiveDelayMs(c, screen) + c.durationMs);\n }\n return maxEnd;\n};\n\n/**\n * Absolute time (ms from screen mount) when layer motion should begin: after all\n * mount/stagger clips end, plus optional author delay.\n */\nexport const layerRestingMotionStartMs = (\n screen: Screen,\n layerId: string,\n cfg: RestingMotion,\n): number =>\n cfg.timelineStartMs !== undefined\n ? cfg.timelineStartMs\n : layerMountClipsEndMs(screen, layerId) + restingMotionDelayAfterMountEndMs(cfg);\n\n/** Latest end (ms) of any layer motion segment (start + segment duration) — for timeline rulers. */\nexport const screenRestingTimelineExtentMs = (screen: Screen): number => {\n let max = 0;\n walkScreen(screen, (l) => {\n for (const r of layerRestingMotionEntries(l)) {\n const start = layerRestingMotionStartMs(screen, l.id, r);\n max = Math.max(max, start + restingMotionEffectiveDurationMs(r));\n }\n });\n return max;\n};\n\n/**\n * Upper bound for editor scrub time (ms from screen mount). Mount/unmount clips\n * alone cap at {@link screenAnimationsDurationMs}; layer motion may start and\n * end later, so the scrub clock must allow times up to this value or motion\n * never becomes \"allowed\" in the preview.\n */\nexport const motionTimelineScrubClampMs = (screen: Screen): number =>\n Math.max(\n screenAnimationsDurationMs(screen),\n screenRestingTimelineExtentMs(screen),\n screenLoaderTimelineExtentMs(screen),\n );\n\n/** Default ruler span when the screen has no timed motion content yet. */\nexport const DEFAULT_WORKBENCH_TIMELINE_MS = 10_000;\n\n/**\n * Animation workbench ruler/scrub length (ms). Grows with clips, resting motion,\n * and timed loader segments; never capped. Uses\n * {@link DEFAULT_WORKBENCH_TIMELINE_MS} only when nothing is scheduled yet.\n */\nexport const workbenchTimelineTotalMs = (screen: Screen): number => {\n const extent = motionTimelineScrubClampMs(screen);\n return extent > 0 ? extent : DEFAULT_WORKBENCH_TIMELINE_MS;\n};\n\nexport const layerRestingMotionEndMs = (\n screen: Screen,\n layerId: string,\n cfg: RestingMotion,\n): number => layerRestingMotionStartMs(screen, layerId, cfg) + restingMotionEffectiveDurationMs(cfg);\n\n/** Pure gate for tests and web scrub (no React): inside the motion segment [start, end). */\nexport const restingMotionAllowedAtTime = (\n screen: Screen,\n layerId: string,\n tMs: number,\n cfg: RestingMotion,\n): boolean => {\n const start = layerRestingMotionStartMs(screen, layerId, cfg);\n const end = start + restingMotionEffectiveDurationMs(cfg);\n return tMs >= start && tMs < end;\n};\n\n/** Elapsed ms inside the motion segment, or null if outside [start, end). */\nexport const restingMotionLocalElapsedMs = (\n screen: Screen,\n layerId: string,\n cfg: RestingMotion,\n tMs: number,\n): number | null => {\n if (!restingMotionAllowedAtTime(screen, layerId, tMs, cfg)) return null;\n return tMs - layerRestingMotionStartMs(screen, layerId, cfg);\n};\n","import type { RestingMotion } from '@getrheo/contracts/layers';\nimport type { Screen } from '@getrheo/contracts/screens';\nimport {\n RESTING_MOTION_DEFAULT_DURATION_MS,\n restingMotionCycleDurationMs,\n restingMotionEffectiveDurationMs,\n restingMotionIntensity,\n type RuntimeStyle,\n} from './restingMotionEntries.js';\nimport {\n restingMotionLocalElapsedMs,\n} from './restingMotionTimeline.js';\n\n/** Peak lift (px) matched to legacy `14 * intensity` when {@link RestingMotion.bounceAmplitudePx} is unset. */\nexport const RESTING_MOTION_BOUNCE_BASE_PX = 14;\n\nexport const restingMotionBounceAmplitudePx = (r: RestingMotion): number =>\n r.bounceAmplitudePx ?? RESTING_MOTION_BOUNCE_BASE_PX * restingMotionIntensity(r);\n\n/** Single bounce arc: 0 → peak → 0 over phase ∈ [0, 1]. Negative Y is upward. */\nexport const bouncePhaseToTranslateY = (ph: number, amplitudePx: number): number =>\n -amplitudePx * Math.sin(Math.PI * ph);\n\n/** Default scale magnitude (%), used when {@link RestingMotion.scalePercent} / legacy fields omitted. */\nexport const RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT = 8;\nexport const RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT = 0;\nexport const RESTING_MOTION_DEFAULT_SCALE_PERCENT = RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;\n\n/** Default peak Y (% of layer box) when translate percent peaks omitted (no legacy px). */\nexport const RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT = 6;\n/** Default legacy peak Y in px when only deprecated {@link RestingMotion.translateRangePx} applies. */\nexport const RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX = 6;\n/** @deprecated Use {@link RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX}. */\nexport const RESTING_MOTION_DEFAULT_TRANSLATE_RANGE_PX = RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PX;\n/** Default peak rotation (°) from rest when {@link RestingMotion.rotateMaxDeg} is omitted. */\nexport const RESTING_MOTION_DEFAULT_ROTATE_DEG = 5;\nexport const RESTING_MOTION_PULSE_DIP_BASE = 0.38;\n\n/** Scale preset: ms for one rest → peak → rest cycle (authoring + native + web fallback). */\nexport const restingMotionScalePatternDurationMs = (r: RestingMotion): number => {\n const def = RESTING_MOTION_DEFAULT_DURATION_MS.scale;\n if (r.preset !== 'scale') return def;\n if (r.loop !== true) {\n return r.durationMs ?? r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;\n }\n return r.scalePatternDurationMs ?? r.cycleDurationMs ?? def;\n};\n\n/**\n * Phase 0..1 for the current motion pattern. Non-loop: one full pattern stretched over segment.\n * Loop: pattern repeats every cycle duration within the segment.\n */\nexport const restingMotionPhase01 = (cfg: RestingMotion, localMs: number): number => {\n const segment = restingMotionEffectiveDurationMs(cfg);\n const cycle = restingMotionCycleDurationMs(cfg);\n if (segment <= 0) return 0;\n\n if (cfg.preset === 'scale') {\n const pattern = restingMotionScalePatternDurationMs(cfg);\n if (pattern <= 0) return 0;\n if (cfg.loop === true) {\n const u = ((localMs % pattern) + pattern) % pattern;\n return u / pattern;\n }\n return Math.min(1, Math.max(0, localMs / pattern));\n }\n\n if (cfg.loop === true && cycle > 0) {\n const u = ((localMs % cycle) + cycle) % cycle;\n return u / cycle;\n }\n return Math.min(1, Math.max(0, localMs / segment));\n};\n\nexport const restingMotionScaleDirection = (r: RestingMotion): 'up' | 'down' => {\n 'worklet';\n if (r.scaleDirection === 'down') return 'down';\n if (r.scaleDirection === 'up') return 'up';\n const down = r.scaleDownPercent ?? 0;\n const up = r.scaleUpPercent ?? 0;\n if (down > 0 && up === 0) return 'down';\n return 'up';\n};\n\nexport const restingMotionScalePercentResolved = (r: RestingMotion): number => {\n 'worklet';\n if (r.scalePercent !== undefined) return r.scalePercent;\n if (restingMotionScaleDirection(r) === 'down') {\n return r.scaleDownPercent !== undefined\n ? r.scaleDownPercent\n : (r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_DOWN_PERCENT);\n }\n return r.scaleUpPercent ?? RESTING_MOTION_DEFAULT_SCALE_UP_PERCENT;\n};\n\nexport const restingMotionScaleAmountFraction = (r: RestingMotion): number => {\n 'worklet';\n return (restingMotionScalePercentResolved(r) / 100) * restingMotionIntensity(r);\n};\n\n/** Scale preset: return to rest after the peak (default). If false, ramp to peak and hold (per cycle when looping). */\nexport const restingMotionScaleSpringBack = (r: RestingMotion): boolean => {\n 'worklet';\n return r.scaleSpringBack !== false;\n};\n\n/** @deprecated Prefer {@link restingMotionScaleAmountFraction} / {@link restingMotionScalePeakMultiplier}. */\nexport const restingMotionScaleUpFraction = (r: RestingMotion): number =>\n restingMotionScaleDirection(r) === 'up' ? restingMotionScaleAmountFraction(r) : 0;\n\n/** @deprecated Prefer {@link restingMotionScaleAmountFraction} / {@link restingMotionScalePeakMultiplier}. */\nexport const restingMotionScaleDownFraction = (r: RestingMotion): number =>\n restingMotionScaleDirection(r) === 'down' ? restingMotionScaleAmountFraction(r) : 0;\n\n/** Peak scale multiplier (1±amount) for CSS vars and authoring; independent of {@link restingMotionScaleSpringBack}. */\nexport const restingMotionScalePeakMultiplier = (cfg: RestingMotion): number => {\n 'worklet';\n const amt = restingMotionScaleAmountFraction(cfg);\n const dir = restingMotionScaleDirection(cfg);\n return dir === 'up' ? 1 + amt : 1 - amt;\n};\n\n/**\n * One scale cycle over ph ∈ [0,1]: rest (1) → peak (1±amount) → rest when spring back is on;\n * linear rest → peak when off (matches rotate ramp semantics).\n */\nexport const restingMotionScaleAtPhase = (cfg: RestingMotion, ph: number): number => {\n 'worklet';\n const peak = restingMotionScalePeakMultiplier(cfg);\n const p = Math.min(1, Math.max(0, ph));\n if (!restingMotionScaleSpringBack(cfg)) {\n return 1 + (peak - 1) * p;\n }\n if (p < 0.5) {\n const t = p * 2;\n return 1 + (peak - 1) * t;\n }\n const t = (p - 0.5) * 2;\n return peak + (1 - peak) * t;\n};\n\nconst legacyTranslatePxToDisplayPercent = (px: number): number =>\n Math.max(-200, Math.min(200, Math.round((px / 80) * 200)));\n\ntype TranslatePeakBase =\n | { unit: 'percent'; x: number; y: number }\n | { unit: 'px'; x: number; y: number };\n\nconst restingMotionTranslateBasePeak = (r: RestingMotion): TranslatePeakBase => {\n 'worklet';\n const hasPercent =\n r.translatePeakXPercent !== undefined || r.translatePeakYPercent !== undefined;\n const hasPx = r.translatePeakXPx !== undefined || r.translatePeakYPx !== undefined;\n const hasLegacyRange = r.translateRangePx !== undefined;\n\n if (hasPercent) {\n return {\n unit: 'percent',\n x: r.translatePeakXPercent ?? 0,\n y: r.translatePeakYPercent ?? 0,\n };\n }\n if (hasPx || hasLegacyRange) {\n if (hasPx) {\n return {\n unit: 'px',\n x: r.translatePeakXPx ?? 0,\n y: r.translatePeakYPx ?? 0,\n };\n }\n return { unit: 'px', x: 0, y: r.translateRangePx ?? 0 };\n }\n return { unit: 'percent', x: 0, y: RESTING_MOTION_DEFAULT_TRANSLATE_PEAK_Y_PERCENT };\n};\n\n/**\n * Authoring translate peaks in % of the layer box (−200–200) for the editor. Maps legacy px fields\n * to a comparable % for display (old ±80 px range → full ±200% scale).\n */\nexport const restingMotionTranslateAuthoringPeakPercent = (r: RestingMotion): { x: number; y: number } => {\n const base = restingMotionTranslateBasePeak(r);\n if (base.unit === 'percent') return { x: base.x, y: base.y };\n return {\n x: legacyTranslatePxToDisplayPercent(base.x),\n y: legacyTranslatePxToDisplayPercent(base.y),\n };\n};\n\n/** @deprecated Use {@link restingMotionTranslateAuthoringPeakPercent} or {@link restingMotionTranslatePeakResolved}. */\nexport const restingMotionTranslateAuthoringPeakPx = (r: RestingMotion): { x: number; y: number } => {\n const base = restingMotionTranslateBasePeak(r);\n if (base.unit === 'px') return { x: base.x, y: base.y };\n return { x: 0, y: 0 };\n};\n\n/** Resolved peak translation for transform: % of layer box, or legacy px. After intensity; clamped. */\nexport const restingMotionTranslatePeakResolved = (\n r: RestingMotion,\n): { unit: 'percent' | 'px'; x: number; y: number } => {\n 'worklet';\n const base = restingMotionTranslateBasePeak(r);\n const i = restingMotionIntensity(r);\n if (base.unit === 'percent') {\n let x = base.x * i;\n let y = base.y * i;\n x = Math.max(-200, Math.min(200, x));\n y = Math.max(-200, Math.min(200, y));\n return { unit: 'percent', x, y };\n }\n let x = base.x * i;\n let y = base.y * i;\n x = Math.max(-200, Math.min(200, x));\n y = Math.max(-200, Math.min(200, y));\n return { unit: 'px', x, y };\n};\n\n/** @deprecated Use {@link restingMotionTranslatePeakResolved}. */\nexport const restingMotionTranslatePeakPx = (r: RestingMotion): { x: number; y: number } => {\n const r2 = restingMotionTranslatePeakResolved(r);\n return { x: r2.x, y: r2.y };\n};\n\n/** Translate preset: spring back to origin after the peak (default). */\nexport const restingMotionTranslateSpringBack = (r: RestingMotion): boolean => {\n 'worklet';\n return r.translateSpringBack !== false;\n};\n\n/** Resolved peak rotation (°), clamped to [0, 360]. Intensity scales the authored angle. */\nexport const restingMotionRotateMaxDeg = (r: RestingMotion): number => {\n 'worklet';\n const raw = (r.rotateMaxDeg ?? RESTING_MOTION_DEFAULT_ROTATE_DEG) * restingMotionIntensity(r);\n return Math.max(0, Math.min(360, raw));\n};\n\n/** Rotate preset: oscillate back to 0° after the peak (default). If false, ramp to peak and hold (per cycle when looping). */\nexport const restingMotionRotateSpringBack = (r: RestingMotion): boolean => {\n 'worklet';\n return r.rotateSpringBack !== false;\n};\n\n/** Rotate preset: authored spin direction (default clockwise). */\nexport const restingMotionRotateDirection = (\n r: RestingMotion,\n): 'clockwise' | 'counterclockwise' => {\n 'worklet';\n return r.rotateDirection === 'counterclockwise' ? 'counterclockwise' : 'clockwise';\n};\n\n/** +1 for clockwise, −1 for counter-clockwise (anti-clockwise). */\nexport const restingMotionRotateSign = (r: RestingMotion): 1 | -1 => {\n 'worklet';\n return restingMotionRotateDirection(r) === 'counterclockwise' ? -1 : 1;\n};\n\nexport const restingMotionPulseMinOpacity = (r: RestingMotion): number => {\n 'worklet';\n if (r.pulseMinOpacity !== undefined) {\n return Math.max(0, Math.min(1, r.pulseMinOpacity));\n }\n return Math.max(\n 0,\n Math.min(1, 1 - RESTING_MOTION_PULSE_DIP_BASE * restingMotionIntensity(r)),\n );\n};\n\n/** Sample motion at phase 0..1 (same geometry as web keyframes / native worklets). */\nexport const restingMotionSampleStyle = (cfg: RestingMotion, ph: number): RuntimeStyle => {\n switch (cfg.preset) {\n case 'translate': {\n const peak = restingMotionTranslatePeakResolved(cfg);\n const env = restingMotionTranslateSpringBack(cfg) ? Math.sin(ph * Math.PI) : ph;\n let tx = env * peak.x;\n let ty = env * peak.y;\n if (Math.abs(tx) < 1e-6) tx = 0;\n if (Math.abs(ty) < 1e-6) ty = 0;\n if (peak.unit === 'percent') {\n return { transform: `translate(${tx}%, ${ty}%)`, willChange: 'transform' };\n }\n return { transform: `translate(${tx}px, ${ty}px)`, willChange: 'transform' };\n }\n case 'bounce': {\n const y = bouncePhaseToTranslateY(ph, restingMotionBounceAmplitudePx(cfg));\n return { transform: `translateY(${y}px)`, willChange: 'transform' };\n }\n case 'scale': {\n const sc = restingMotionScaleAtPhase(cfg, ph);\n return { transform: `scale(${sc})`, willChange: 'transform' };\n }\n case 'pulse': {\n const omin = restingMotionPulseMinOpacity(cfg);\n const dip = 1 - omin;\n const op = ph <= 0.5 ? 1 - ph * 2 * dip : 1 - (1 - ph) * 2 * dip;\n return { opacity: op, willChange: 'opacity' };\n }\n case 'rotate': {\n const peakDeg = restingMotionRotateMaxDeg(cfg);\n let deg =\n restingMotionRotateSign(cfg) *\n (restingMotionRotateSpringBack(cfg)\n ? Math.sin(ph * Math.PI) * peakDeg\n : ph * peakDeg);\n // sin(π) is not exactly 0 in JS — snap so endpoints read as 0° and match keyframes.\n if (Math.abs(deg) < 1e-6) deg = 0;\n return { transform: `rotate(${deg}deg)`, willChange: 'transform' };\n }\n default:\n return {};\n }\n};\n\n/** Driven by timeline clock (editor / scrub). Returns null outside the motion segment. */\nexport const restingMotionStyleAtTime = (\n screen: Screen,\n layerId: string,\n cfg: RestingMotion,\n tMs: number,\n): RuntimeStyle | null => {\n const local = restingMotionLocalElapsedMs(screen, layerId, cfg, tMs);\n if (local === null) return null;\n const ph = restingMotionPhase01(cfg, local);\n return restingMotionSampleStyle(cfg, ph);\n};\n\n/**\n * Injected once in web sim DOM. Intensity scales via `--ob-rm-i` on the\n * animated element (default 1).\n */\nexport const RESTING_MOTION_KEYFRAMES_CSS = `\n@keyframes ob-rm-translate {\n 0%, 100% { transform: translate(0, 0); }\n 50% {\n transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));\n }\n}\n@keyframes ob-rm-translate-ramp {\n 0% { transform: translate(0, 0); }\n 100% {\n transform: translate(var(--ob-rm-translate-peak-x, 0%), var(--ob-rm-translate-peak-y, 6%));\n }\n}\n@keyframes ob-rm-bounce {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(calc(-1 * var(--ob-rm-bounce-px, 14px))); }\n}\n@keyframes ob-rm-scale {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }\n}\n@keyframes ob-rm-scale-ramp {\n 0% { transform: scale(1); }\n 100% { transform: scale(var(--ob-rm-scale-peak, 1.08)); }\n}\n@keyframes ob-rm-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: var(--ob-rm-pulse-min, 0.62); }\n}\n@keyframes ob-rm-rotate {\n 0%, 100% { transform: rotate(0deg); }\n 50% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }\n}\n@keyframes ob-rm-rotate-ramp {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(var(--ob-rm-rotate-peak, 5deg)); }\n}\n`.trim();\n\nconst restingMotionWebAnimationName = (config: RestingMotion): string => {\n if (config.preset === 'rotate' && !restingMotionRotateSpringBack(config)) {\n return 'ob-rm-rotate-ramp';\n }\n if (config.preset === 'translate' && !restingMotionTranslateSpringBack(config)) {\n return 'ob-rm-translate-ramp';\n }\n if (config.preset === 'scale' && !restingMotionScaleSpringBack(config)) {\n return 'ob-rm-scale-ramp';\n }\n return `ob-rm-${config.preset}`;\n};\n\n/**\n * Fallback when no motion clock is available (e.g. static export). Prefer\n * {@link restingMotionStyleAtTime} in the editor where the timeline drives sampling.\n */\nexport const restingMotionWebStyle = (config: RestingMotion): RuntimeStyle | null => {\n const duration = restingMotionEffectiveDurationMs(config);\n const intensity = restingMotionIntensity(config);\n const name = restingMotionWebAnimationName(config);\n const oneCycleMs =\n config.preset === 'scale'\n ? restingMotionScalePatternDurationMs(config)\n : config.loop === true\n ? restingMotionCycleDurationMs(config)\n : duration;\n const iter = config.loop === true ? 'infinite' : '1 forwards';\n const base = {\n ['--ob-rm-i']: String(intensity),\n animation: `${name} ${oneCycleMs}ms ease-in-out ${iter}`,\n willChange: config.preset === 'pulse' ? 'opacity' : 'transform',\n } as RuntimeStyle;\n if (config.preset === 'bounce') {\n return {\n ...base,\n ['--ob-rm-bounce-px']: `${restingMotionBounceAmplitudePx(config)}px`,\n } as RuntimeStyle;\n }\n if (config.preset === 'scale') {\n return {\n ...base,\n ['--ob-rm-scale-peak']: String(restingMotionScalePeakMultiplier(config)),\n } as RuntimeStyle;\n }\n if (config.preset === 'translate') {\n const peak = restingMotionTranslatePeakResolved(config);\n const sx = peak.unit === 'percent' ? `${peak.x}%` : `${peak.x}px`;\n const sy = peak.unit === 'percent' ? `${peak.y}%` : `${peak.y}px`;\n return {\n ...base,\n ['--ob-rm-translate-peak-x']: sx,\n ['--ob-rm-translate-peak-y']: sy,\n } as RuntimeStyle;\n }\n if (config.preset === 'rotate') {\n const signedPeak = restingMotionRotateSign(config) * restingMotionRotateMaxDeg(config);\n return {\n ...base,\n ['--ob-rm-rotate-peak']: `${signedPeak}deg`,\n } as RuntimeStyle;\n }\n if (config.preset === 'pulse') {\n return {\n ...base,\n ['--ob-rm-pulse-min']: String(restingMotionPulseMinOpacity(config)),\n } as RuntimeStyle;\n }\n return base;\n};\n"]}
|