@funnelsgrove/runtime 0.1.18 → 0.1.19
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/config/builder-preview.protocol.d.ts +5 -0
- package/dist/config/builder-preview.protocol.js +1 -0
- package/dist/runtime/preview-bridge.d.ts +4 -0
- package/dist/runtime/preview-bridge.js +33 -1
- package/dist/runtime/use-funnel-flow-controller.d.ts +10 -1
- package/dist/runtime/use-funnel-flow-controller.js +13 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ export declare const BUILDER_PREVIEW_RUNTIME_MODE_CHANGED = "builder.preview.run
|
|
|
5
5
|
export declare const BUILDER_PREVIEW_DEFINITION_PATCH = "builder.preview.definitionPatch";
|
|
6
6
|
export declare const BUILDER_PREVIEW_VARIABLE_VALUES_CHANGED = "builder.preview.variableValuesChanged";
|
|
7
7
|
export declare const BUILDER_PREVIEW_PAYWALL_PLANS_CHANGED = "builder.preview.paywallPlansChanged";
|
|
8
|
+
export declare const BUILDER_PREVIEW_THEME_CHANGED = "builder.preview.themeChanged";
|
|
8
9
|
export type BuilderPreviewReadyMessage = {
|
|
9
10
|
type: typeof BUILDER_PREVIEW_READY;
|
|
10
11
|
stepId: string;
|
|
@@ -75,3 +76,7 @@ export type BuilderPreviewPaywallPlansChangedMessage = {
|
|
|
75
76
|
stepId: string;
|
|
76
77
|
plans: BuilderPreviewPaywallPlan[];
|
|
77
78
|
};
|
|
79
|
+
export type BuilderPreviewThemeChangedMessage = {
|
|
80
|
+
type: typeof BUILDER_PREVIEW_THEME_CHANGED;
|
|
81
|
+
cssVariables: Record<string, string>;
|
|
82
|
+
};
|
|
@@ -5,3 +5,4 @@ export const BUILDER_PREVIEW_RUNTIME_MODE_CHANGED = 'builder.preview.runtimeMode
|
|
|
5
5
|
export const BUILDER_PREVIEW_DEFINITION_PATCH = 'builder.preview.definitionPatch';
|
|
6
6
|
export const BUILDER_PREVIEW_VARIABLE_VALUES_CHANGED = 'builder.preview.variableValuesChanged';
|
|
7
7
|
export const BUILDER_PREVIEW_PAYWALL_PLANS_CHANGED = 'builder.preview.paywallPlansChanged';
|
|
8
|
+
export const BUILDER_PREVIEW_THEME_CHANGED = 'builder.preview.themeChanged';
|
|
@@ -41,12 +41,16 @@ type PreviewBridgeMessage = {
|
|
|
41
41
|
kind: 'paywallPlansChanged';
|
|
42
42
|
stepId: string;
|
|
43
43
|
plans: BuilderPreviewPaywallPlan[];
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'themeChanged';
|
|
46
|
+
cssVariables: Record<string, string>;
|
|
44
47
|
} | {
|
|
45
48
|
kind: 'goToStep';
|
|
46
49
|
stepId: string;
|
|
47
50
|
};
|
|
48
51
|
export declare const parsePreviewQuickEditPatch: (value: unknown) => PreviewQuickEditPatch | null;
|
|
49
52
|
export declare const parsePreviewBridgeMessage: (value: unknown) => PreviewBridgeMessage | null;
|
|
53
|
+
export declare const applyPreviewThemeVariables: (cssVariables: Record<string, string>) => void;
|
|
50
54
|
export declare const applyPreviewQuickEditPatch: (patch: PreviewQuickEditPatch) => void;
|
|
51
55
|
export declare function emitPreviewVariableValues(stepId: FunnelStepId, values: Record<string, string>): void;
|
|
52
56
|
export declare function usePreviewVariableValues(stepId: FunnelStepId, values: Record<string, string>): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
|
-
import { BUILDER_PREVIEW_ACTIVE_STEP_CHANGED, BUILDER_PREVIEW_DEFINITION_PATCH, BUILDER_PREVIEW_GO_TO_STEP, BUILDER_PREVIEW_PAYWALL_PLANS_CHANGED, BUILDER_PREVIEW_READY, BUILDER_PREVIEW_RUNTIME_MODE_CHANGED, BUILDER_PREVIEW_VARIABLE_VALUES_CHANGED, } from '../config/builder-preview.protocol.js';
|
|
3
|
+
import { BUILDER_PREVIEW_ACTIVE_STEP_CHANGED, BUILDER_PREVIEW_DEFINITION_PATCH, BUILDER_PREVIEW_GO_TO_STEP, BUILDER_PREVIEW_PAYWALL_PLANS_CHANGED, BUILDER_PREVIEW_READY, BUILDER_PREVIEW_RUNTIME_MODE_CHANGED, BUILDER_PREVIEW_THEME_CHANGED, BUILDER_PREVIEW_VARIABLE_VALUES_CHANGED, } from '../config/builder-preview.protocol.js';
|
|
4
4
|
import { isPreviewFrameRuntime } from '../services/preview-frame.service.js';
|
|
5
5
|
import { logger } from '../services/logger.js';
|
|
6
6
|
import { applyPreviewDefinitionPatch, applyPreviewPaywallPlansPatch } from './preview-definition-overrides.js';
|
|
@@ -107,6 +107,13 @@ export const parsePreviewQuickEditPatch = (value) => {
|
|
|
107
107
|
}
|
|
108
108
|
return null;
|
|
109
109
|
};
|
|
110
|
+
const parseThemeCssVariables = (value) => {
|
|
111
|
+
if (!isRecord(value)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const cssVariables = Object.fromEntries(Object.entries(value).filter((entry) => entry[0].startsWith('--') && typeof entry[1] === 'string'));
|
|
115
|
+
return Object.keys(cssVariables).length > 0 ? cssVariables : null;
|
|
116
|
+
};
|
|
110
117
|
export const parsePreviewBridgeMessage = (value) => {
|
|
111
118
|
if (!isRecord(value)) {
|
|
112
119
|
return null;
|
|
@@ -149,12 +156,33 @@ export const parsePreviewBridgeMessage = (value) => {
|
|
|
149
156
|
}
|
|
150
157
|
: null;
|
|
151
158
|
}
|
|
159
|
+
if (messageType === BUILDER_PREVIEW_THEME_CHANGED) {
|
|
160
|
+
const cssVariables = parseThemeCssVariables(value.cssVariables);
|
|
161
|
+
return cssVariables ? { kind: 'themeChanged', cssVariables } : null;
|
|
162
|
+
}
|
|
152
163
|
if (messageType === BUILDER_PREVIEW_GO_TO_STEP) {
|
|
153
164
|
const stepId = typeof value.stepId === 'string' ? value.stepId.trim() : '';
|
|
154
165
|
return stepId ? { kind: 'goToStep', stepId } : null;
|
|
155
166
|
}
|
|
156
167
|
return null;
|
|
157
168
|
};
|
|
169
|
+
export const applyPreviewThemeVariables = (cssVariables) => {
|
|
170
|
+
if (typeof document === 'undefined') {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const targets = [
|
|
174
|
+
document.documentElement,
|
|
175
|
+
document.body,
|
|
176
|
+
...Array.from(document.querySelectorAll('.page-root')),
|
|
177
|
+
].filter((target) => Boolean(target));
|
|
178
|
+
for (const target of targets) {
|
|
179
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
180
|
+
if (key.startsWith('--')) {
|
|
181
|
+
target.style.setProperty(key, value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
158
186
|
export const applyPreviewQuickEditPatch = (patch) => {
|
|
159
187
|
if (typeof document === 'undefined') {
|
|
160
188
|
return;
|
|
@@ -291,6 +319,10 @@ export function usePreviewBridge({ activeStepId, onGoToStep, resolveRenderableSt
|
|
|
291
319
|
applyPreviewPaywallPlansPatch(action.stepId, action.plans);
|
|
292
320
|
return;
|
|
293
321
|
}
|
|
322
|
+
if (action.kind === 'themeChanged') {
|
|
323
|
+
applyPreviewThemeVariables(action.cssVariables);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
294
326
|
if (action.kind === 'runtimeModeChanged') {
|
|
295
327
|
setRuntimeMode(action.mode);
|
|
296
328
|
return;
|
|
@@ -9,7 +9,16 @@ export type FunnelFlowControllerExperiment<StepId extends string> = FunnelManife
|
|
|
9
9
|
})[];
|
|
10
10
|
};
|
|
11
11
|
type StepComponentRegistry = Record<string, unknown>;
|
|
12
|
+
type FunnelFlowAnalyticsAdapter = {
|
|
13
|
+
trackFirstStepViewed?: (input: {
|
|
14
|
+
stepId: string;
|
|
15
|
+
stepName: string;
|
|
16
|
+
occurredAt: string;
|
|
17
|
+
featureFlags?: Record<string, string>;
|
|
18
|
+
}) => string | null;
|
|
19
|
+
};
|
|
12
20
|
type UseFunnelFlowControllerInput<StepId extends string> = {
|
|
21
|
+
analytics?: FunnelFlowAnalyticsAdapter;
|
|
13
22
|
initialStepId?: StepId;
|
|
14
23
|
lockToInitialStep?: boolean;
|
|
15
24
|
defaultStepId: StepId;
|
|
@@ -63,5 +72,5 @@ export declare function computeRenderSuspended(input: {
|
|
|
63
72
|
}>;
|
|
64
73
|
isPreviewRuntime?: boolean;
|
|
65
74
|
}): boolean;
|
|
66
|
-
export declare function useFunnelFlowController<StepId extends string>({ initialStepId, lockToInitialStep, defaultStepId, stepSequence, stepById, stepComponentById, funnelExperiments, getPathForStep, getStepIdFromPath, getSequentialNextStepId, getChoiceTargetsForStep, isFunnelStepId, resolveConfiguredNextStep, resolveRuntimeInitialStepId, }: UseFunnelFlowControllerInput<StepId>): UseFunnelFlowControllerResult<StepId>;
|
|
75
|
+
export declare function useFunnelFlowController<StepId extends string>({ analytics, initialStepId, lockToInitialStep, defaultStepId, stepSequence, stepById, stepComponentById, funnelExperiments, getPathForStep, getStepIdFromPath, getSequentialNextStepId, getChoiceTargetsForStep, isFunnelStepId, resolveConfiguredNextStep, resolveRuntimeInitialStepId, }: UseFunnelFlowControllerInput<StepId>): UseFunnelFlowControllerResult<StepId>;
|
|
67
76
|
export {};
|
|
@@ -54,7 +54,7 @@ export function computeRenderSuspended(input) {
|
|
|
54
54
|
}
|
|
55
55
|
return input.experiments.some((experiment) => experiment.stepId === input.activeStepId);
|
|
56
56
|
}
|
|
57
|
-
export function useFunnelFlowController({ initialStepId, lockToInitialStep = false, defaultStepId, stepSequence, stepById, stepComponentById, funnelExperiments, getPathForStep, getStepIdFromPath, getSequentialNextStepId, getChoiceTargetsForStep, isFunnelStepId, resolveConfiguredNextStep, resolveRuntimeInitialStepId, }) {
|
|
57
|
+
export function useFunnelFlowController({ analytics, initialStepId, lockToInitialStep = false, defaultStepId, stepSequence, stepById, stepComponentById, funnelExperiments, getPathForStep, getStepIdFromPath, getSequentialNextStepId, getChoiceTargetsForStep, isFunnelStepId, resolveConfiguredNextStep, resolveRuntimeInitialStepId, }) {
|
|
58
58
|
var _a, _b;
|
|
59
59
|
const [runtimeMode, setRuntimeMode] = useRuntimeMode();
|
|
60
60
|
const [editorModeEnabled, setEditorModeEnabled] = useState(false);
|
|
@@ -106,6 +106,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
106
106
|
const attributesRef = useRef(attributes);
|
|
107
107
|
const postHogFeatureFlagsRef = useRef({});
|
|
108
108
|
const prevStepIdRef = useRef(null);
|
|
109
|
+
const firstStepViewedTrackedRef = useRef(false);
|
|
109
110
|
const stepStartedAtByIdRef = useRef({});
|
|
110
111
|
const engagedStepIdsRef = useRef(new Set());
|
|
111
112
|
const currentUserIdRef = useRef(user.id);
|
|
@@ -368,6 +369,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
368
369
|
recordStepCompletion(stepId, choices);
|
|
369
370
|
}, [recordStepCompletion]);
|
|
370
371
|
useEffect(() => {
|
|
372
|
+
var _a;
|
|
371
373
|
if (safeActiveStepId !== activeStepId) {
|
|
372
374
|
logger.warn(`[FunnelFlow] Unknown or non-renderable step "${activeStepId}", falling back to "${safeActiveStepId}".`);
|
|
373
375
|
setActiveStepId(safeActiveStepId);
|
|
@@ -391,6 +393,15 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
391
393
|
startedAt,
|
|
392
394
|
});
|
|
393
395
|
capturePostHogStepEvent('step_started', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: renderedStepId, step_name: stepName, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
|
|
396
|
+
if (!firstStepViewedTrackedRef.current && safeActiveStepId === defaultStepId) {
|
|
397
|
+
firstStepViewedTrackedRef.current = true;
|
|
398
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackFirstStepViewed) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
399
|
+
stepId: renderedStepId,
|
|
400
|
+
stepName,
|
|
401
|
+
occurredAt: startedAt,
|
|
402
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
394
405
|
}
|
|
395
406
|
attributesAtStepStart.current = Object.assign({}, attributesRef.current);
|
|
396
407
|
prevStepIdRef.current = renderedStepId;
|
|
@@ -425,6 +436,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
425
436
|
return () => window.clearTimeout(timer);
|
|
426
437
|
}, [
|
|
427
438
|
activeStepId,
|
|
439
|
+
analytics,
|
|
428
440
|
defaultStepId,
|
|
429
441
|
isPreviewRuntime,
|
|
430
442
|
recordStepCompletion,
|