@funnelsgrove/runtime 0.1.0 → 0.1.2
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/README.md +20 -1
- package/dist/components/FunnelContext.d.ts +5 -2
- package/dist/components/FunnelContext.js +3 -0
- package/dist/components/FunnelEditorPanel.d.ts +3 -5
- package/dist/components/FunnelEditorPanel.js +3 -3
- package/dist/components/ManageSubscriptionScreen.d.ts +51 -0
- package/dist/components/ManageSubscriptionScreen.js +349 -0
- package/dist/components/RuntimeDevInfoBox.d.ts +23 -0
- package/dist/components/RuntimeDevInfoBox.js +363 -0
- package/dist/components/SubscriptionHandoffScreen.d.ts +31 -0
- package/dist/components/SubscriptionHandoffScreen.js +338 -0
- package/dist/config/builder-preview.protocol.d.ts +73 -0
- package/dist/config/builder-preview.protocol.js +3 -0
- package/dist/config/env.config.d.ts +44 -0
- package/dist/config/env.config.js +161 -0
- package/dist/config/font-config.d.ts +14 -0
- package/dist/config/font-config.js +101 -0
- package/dist/config/funnel-theme.d.ts +61 -10
- package/dist/config/funnel-theme.js +355 -35
- package/dist/config/funnel.manifest.types.d.ts +13 -7
- package/dist/content/step-content.d.ts +130 -0
- package/dist/content/step-content.js +381 -0
- package/dist/index.d.ts +33 -21
- package/dist/index.js +33 -21
- package/dist/runtime/browser-helpers.d.ts +1 -0
- package/dist/runtime/browser-helpers.js +14 -0
- package/dist/runtime/experiment-assignment.d.ts +13 -4
- package/dist/runtime/experiment-assignment.js +9 -27
- package/dist/runtime/funnel-attribution.d.ts +18 -0
- package/dist/runtime/funnel-attribution.js +226 -0
- package/dist/runtime/funnel-flow.d.ts +9 -10
- package/dist/runtime/funnel-flow.js +4 -18
- package/dist/runtime/funnel-manifest.validation.d.ts +1 -1
- package/dist/runtime/funnel-manifest.validation.js +2 -6
- package/dist/runtime/funnel-runtime.d.ts +2 -3
- package/dist/runtime/funnel-runtime.js +6 -13
- package/dist/runtime/posthog-flags.d.ts +30 -0
- package/dist/runtime/posthog-flags.js +71 -0
- package/dist/runtime/preview-bridge.d.ts +12 -2
- package/dist/runtime/preview-bridge.js +95 -4
- package/dist/runtime/preview-definition-overrides.d.ts +20 -0
- package/dist/runtime/preview-definition-overrides.js +148 -0
- package/dist/runtime/route-resolver.d.ts +2 -3
- package/dist/runtime/route-resolver.js +15 -26
- package/dist/runtime/subscription-handoff.d.ts +32 -0
- package/dist/runtime/subscription-handoff.js +113 -0
- package/dist/runtime/use-funnel-flow-controller.d.ts +19 -10
- package/dist/runtime/use-funnel-flow-controller.js +190 -159
- package/dist/sdk/userAnswers.d.ts +2 -2
- package/dist/services/api.service.d.ts +21 -4
- package/dist/services/api.service.js +165 -35
- package/dist/services/funnel-state.service.d.ts +8 -0
- package/dist/services/funnel-state.service.js +44 -0
- package/dist/services/preview-frame.service.d.ts +2 -2
- package/dist/services/preview-frame.service.js +2 -2
- package/dist/services/public-env.d.ts +69 -0
- package/dist/services/public-env.js +105 -0
- package/dist/services/runtime-api.config.d.ts +5 -0
- package/dist/services/runtime-api.config.js +12 -7
- package/dist/services/runtime-mode.service.d.ts +3 -0
- package/dist/services/runtime-mode.service.js +142 -4
- package/package.json +8 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const FONT_REGISTRY = [
|
|
2
|
+
{
|
|
3
|
+
name: 'SF Pro Text',
|
|
4
|
+
category: 'sans-serif',
|
|
5
|
+
fallbacks: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
6
|
+
weights: [400, 500, 600, 700, 800],
|
|
7
|
+
hasItalic: true,
|
|
8
|
+
source: 'system',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'Inter',
|
|
12
|
+
category: 'sans-serif',
|
|
13
|
+
fallbacks: '"Segoe UI", sans-serif',
|
|
14
|
+
weights: [400, 500, 600, 700, 800],
|
|
15
|
+
hasItalic: true,
|
|
16
|
+
source: 'google',
|
|
17
|
+
googleFontFamily: 'Inter',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'Roboto',
|
|
21
|
+
category: 'sans-serif',
|
|
22
|
+
fallbacks: '"Helvetica Neue", Arial, sans-serif',
|
|
23
|
+
weights: [400, 500, 700],
|
|
24
|
+
hasItalic: true,
|
|
25
|
+
source: 'google',
|
|
26
|
+
googleFontFamily: 'Roboto',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Open Sans',
|
|
30
|
+
category: 'sans-serif',
|
|
31
|
+
fallbacks: '"Helvetica Neue", Arial, sans-serif',
|
|
32
|
+
weights: [400, 500, 600, 700, 800],
|
|
33
|
+
hasItalic: true,
|
|
34
|
+
source: 'google',
|
|
35
|
+
googleFontFamily: 'Open+Sans',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Lato',
|
|
39
|
+
category: 'sans-serif',
|
|
40
|
+
fallbacks: '"Helvetica Neue", Arial, sans-serif',
|
|
41
|
+
weights: [400, 700],
|
|
42
|
+
hasItalic: true,
|
|
43
|
+
source: 'google',
|
|
44
|
+
googleFontFamily: 'Lato',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Poppins',
|
|
48
|
+
category: 'sans-serif',
|
|
49
|
+
fallbacks: '"Segoe UI", sans-serif',
|
|
50
|
+
weights: [400, 500, 600, 700],
|
|
51
|
+
hasItalic: true,
|
|
52
|
+
source: 'google',
|
|
53
|
+
googleFontFamily: 'Poppins',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Montserrat',
|
|
57
|
+
category: 'sans-serif',
|
|
58
|
+
fallbacks: '"Helvetica Neue", sans-serif',
|
|
59
|
+
weights: [400, 500, 600, 700, 800],
|
|
60
|
+
hasItalic: true,
|
|
61
|
+
source: 'google',
|
|
62
|
+
googleFontFamily: 'Montserrat',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Playfair Display',
|
|
66
|
+
category: 'serif',
|
|
67
|
+
fallbacks: 'Georgia, "Times New Roman", serif',
|
|
68
|
+
weights: [400, 500, 600, 700],
|
|
69
|
+
hasItalic: true,
|
|
70
|
+
source: 'google',
|
|
71
|
+
googleFontFamily: 'Playfair+Display',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'Philosopher',
|
|
75
|
+
category: 'serif',
|
|
76
|
+
fallbacks: 'Georgia, serif',
|
|
77
|
+
weights: [400, 700],
|
|
78
|
+
hasItalic: true,
|
|
79
|
+
source: 'google',
|
|
80
|
+
googleFontFamily: 'Philosopher',
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
export const getFontRegistry = () => {
|
|
84
|
+
return FONT_REGISTRY;
|
|
85
|
+
};
|
|
86
|
+
export const getFontByName = (name) => {
|
|
87
|
+
return FONT_REGISTRY.find((f) => f.name === name);
|
|
88
|
+
};
|
|
89
|
+
export const buildFontFamilyString = (font) => {
|
|
90
|
+
var _a;
|
|
91
|
+
const def = getFontByName(font.family);
|
|
92
|
+
const fallbacks = (_a = def === null || def === void 0 ? void 0 : def.fallbacks) !== null && _a !== void 0 ? _a : font.fallbacks;
|
|
93
|
+
return `"${font.family}", ${fallbacks}`;
|
|
94
|
+
};
|
|
95
|
+
export const buildGoogleFontsUrl = (font) => {
|
|
96
|
+
const def = getFontByName(font.family);
|
|
97
|
+
if (!def || def.source !== 'google' || !def.googleFontFamily)
|
|
98
|
+
return null;
|
|
99
|
+
const weights = def.weights.join(';');
|
|
100
|
+
return `https://fonts.googleapis.com/css2?family=${def.googleFontFamily}:wght@${weights}&display=swap`;
|
|
101
|
+
};
|
|
@@ -1,14 +1,60 @@
|
|
|
1
|
-
import type { FunnelManifest } from './funnel.manifest.types';
|
|
2
|
-
export type
|
|
1
|
+
import type { FunnelManifest } from './funnel.manifest.types.js';
|
|
2
|
+
export type FunnelThemeInterfaceColors = {
|
|
3
3
|
background: string;
|
|
4
|
+
primary: string;
|
|
5
|
+
primaryText: string;
|
|
6
|
+
secondary: string;
|
|
7
|
+
secondaryText: string;
|
|
4
8
|
text: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
};
|
|
10
|
+
export type FunnelThemeInterfaceOpacity = Record<keyof FunnelThemeInterfaceColors, number>;
|
|
11
|
+
export type FunnelThemeColors = {
|
|
12
|
+
interface: FunnelThemeInterfaceColors;
|
|
13
|
+
interfaceOpacity: FunnelThemeInterfaceOpacity;
|
|
14
|
+
semantic: {
|
|
15
|
+
accent: string;
|
|
16
|
+
button: string;
|
|
17
|
+
buttonText: string;
|
|
18
|
+
surface: string;
|
|
19
|
+
border: string;
|
|
20
|
+
warning: string;
|
|
21
|
+
danger: string;
|
|
22
|
+
};
|
|
23
|
+
derived: {
|
|
24
|
+
bgMuted: string;
|
|
25
|
+
bgSuccess: string;
|
|
26
|
+
bgWarning: string;
|
|
27
|
+
bgDanger: string;
|
|
28
|
+
bgNeutral: string;
|
|
29
|
+
textMuted: string;
|
|
30
|
+
accentSoft: string;
|
|
31
|
+
accentMuted: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type FunnelThemeDarkOverrides = {
|
|
35
|
+
interface?: Partial<FunnelThemeInterfaceColors>;
|
|
36
|
+
interfaceOpacity?: Partial<FunnelThemeColors['interfaceOpacity']>;
|
|
37
|
+
semantic?: Partial<FunnelThemeColors['semantic']>;
|
|
38
|
+
derived?: Partial<FunnelThemeColors['derived']>;
|
|
39
|
+
};
|
|
40
|
+
export type FunnelThemeDevicePreset = {
|
|
41
|
+
name: string;
|
|
42
|
+
shellWidth: number;
|
|
43
|
+
maxWidth: number;
|
|
44
|
+
};
|
|
45
|
+
export type FunnelThemeDeviceSize = {
|
|
46
|
+
activePreset: string;
|
|
47
|
+
presets: FunnelThemeDevicePreset[];
|
|
48
|
+
padding: number;
|
|
49
|
+
cornerRadius: number;
|
|
50
|
+
};
|
|
51
|
+
export type FunnelThemeFont = {
|
|
52
|
+
family: string;
|
|
53
|
+
style: 'Normal' | 'Italic';
|
|
54
|
+
fallbacks: string;
|
|
9
55
|
};
|
|
10
56
|
export type FunnelThemeTypography = {
|
|
11
|
-
|
|
57
|
+
font: FunnelThemeFont;
|
|
12
58
|
fontSizes: {
|
|
13
59
|
xs: string;
|
|
14
60
|
sm: string;
|
|
@@ -20,7 +66,9 @@ export type FunnelThemeTypography = {
|
|
|
20
66
|
fontWeights: {
|
|
21
67
|
regular: number;
|
|
22
68
|
medium: number;
|
|
69
|
+
semibold: number;
|
|
23
70
|
bold: number;
|
|
71
|
+
heavy: number;
|
|
24
72
|
};
|
|
25
73
|
lineHeights: {
|
|
26
74
|
tight: number;
|
|
@@ -29,8 +77,6 @@ export type FunnelThemeTypography = {
|
|
|
29
77
|
};
|
|
30
78
|
};
|
|
31
79
|
export type FunnelThemeLayout = {
|
|
32
|
-
shellWidth: number;
|
|
33
|
-
maxWidth: number;
|
|
34
80
|
safeAreaOffsets: {
|
|
35
81
|
top: number;
|
|
36
82
|
right: number;
|
|
@@ -39,8 +85,11 @@ export type FunnelThemeLayout = {
|
|
|
39
85
|
};
|
|
40
86
|
};
|
|
41
87
|
export type FunnelTheme = {
|
|
88
|
+
interfaceMode: 'light' | 'dark';
|
|
42
89
|
colors: FunnelThemeColors;
|
|
90
|
+
darkOverrides: FunnelThemeDarkOverrides;
|
|
43
91
|
typography: FunnelThemeTypography;
|
|
92
|
+
deviceSize: FunnelThemeDeviceSize;
|
|
44
93
|
layout: FunnelThemeLayout;
|
|
45
94
|
};
|
|
46
95
|
export type FunnelRuntimeDefaults = {
|
|
@@ -56,4 +105,6 @@ export type PublishedFunnelSnapshot = {
|
|
|
56
105
|
};
|
|
57
106
|
export declare const defaultFunnelTheme: FunnelTheme;
|
|
58
107
|
export declare const defaultFunnelRuntimeDefaults: FunnelRuntimeDefaults;
|
|
59
|
-
export declare const
|
|
108
|
+
export declare const normalizeFunnelTheme: (theme: unknown) => FunnelTheme;
|
|
109
|
+
export declare const resolveColors: (theme: FunnelTheme, mode: "light" | "dark") => FunnelThemeColors;
|
|
110
|
+
export declare const createThemeCssVariables: (theme: FunnelTheme, mode?: "light" | "dark") => Record<string, string>;
|
|
@@ -1,14 +1,50 @@
|
|
|
1
|
+
// --- Default theme ---
|
|
1
2
|
export const defaultFunnelTheme = {
|
|
3
|
+
interfaceMode: 'light',
|
|
2
4
|
colors: {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
interface: {
|
|
6
|
+
background: '#f4effd',
|
|
7
|
+
primary: '#ff9332',
|
|
8
|
+
primaryText: '#ffffff',
|
|
9
|
+
secondary: '#3f51b5',
|
|
10
|
+
secondaryText: '#ffffff',
|
|
11
|
+
text: '#404040',
|
|
12
|
+
},
|
|
13
|
+
interfaceOpacity: {
|
|
14
|
+
background: 100,
|
|
15
|
+
primary: 100,
|
|
16
|
+
primaryText: 100,
|
|
17
|
+
secondary: 100,
|
|
18
|
+
secondaryText: 100,
|
|
19
|
+
text: 100,
|
|
20
|
+
},
|
|
21
|
+
semantic: {
|
|
22
|
+
accent: '#3f51b5',
|
|
23
|
+
button: '#4db53f',
|
|
24
|
+
buttonText: '#ffffff',
|
|
25
|
+
surface: '#ffffff',
|
|
26
|
+
border: '#dce2ff',
|
|
27
|
+
warning: '#f28100',
|
|
28
|
+
danger: '#dd3333',
|
|
29
|
+
},
|
|
30
|
+
derived: {
|
|
31
|
+
bgMuted: '#f5f7fb',
|
|
32
|
+
bgSuccess: '#f0f7f1',
|
|
33
|
+
bgWarning: '#fffdf9',
|
|
34
|
+
bgDanger: '#fff5f2',
|
|
35
|
+
bgNeutral: '#f4f1ef',
|
|
36
|
+
textMuted: '#4d5470',
|
|
37
|
+
accentSoft: 'rgb(63 81 181 / 15%)',
|
|
38
|
+
accentMuted: '#a1a9d6',
|
|
39
|
+
},
|
|
9
40
|
},
|
|
41
|
+
darkOverrides: {},
|
|
10
42
|
typography: {
|
|
11
|
-
|
|
43
|
+
font: {
|
|
44
|
+
family: 'SF Pro Text',
|
|
45
|
+
style: 'Normal',
|
|
46
|
+
fallbacks: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
47
|
+
},
|
|
12
48
|
fontSizes: {
|
|
13
49
|
xs: '12px',
|
|
14
50
|
sm: '14px',
|
|
@@ -20,7 +56,9 @@ export const defaultFunnelTheme = {
|
|
|
20
56
|
fontWeights: {
|
|
21
57
|
regular: 400,
|
|
22
58
|
medium: 500,
|
|
59
|
+
semibold: 600,
|
|
23
60
|
bold: 700,
|
|
61
|
+
heavy: 800,
|
|
24
62
|
},
|
|
25
63
|
lineHeights: {
|
|
26
64
|
tight: 1.2,
|
|
@@ -28,15 +66,14 @@ export const defaultFunnelTheme = {
|
|
|
28
66
|
relaxed: 1.65,
|
|
29
67
|
},
|
|
30
68
|
},
|
|
69
|
+
deviceSize: {
|
|
70
|
+
activePreset: 'Universal',
|
|
71
|
+
presets: [{ name: 'Universal', shellWidth: 430, maxWidth: 430 }],
|
|
72
|
+
padding: 16,
|
|
73
|
+
cornerRadius: 16,
|
|
74
|
+
},
|
|
31
75
|
layout: {
|
|
32
|
-
|
|
33
|
-
maxWidth: 430,
|
|
34
|
-
safeAreaOffsets: {
|
|
35
|
-
top: 0,
|
|
36
|
-
right: 0,
|
|
37
|
-
bottom: 0,
|
|
38
|
-
left: 0,
|
|
39
|
-
},
|
|
76
|
+
safeAreaOffsets: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
40
77
|
},
|
|
41
78
|
};
|
|
42
79
|
export const defaultFunnelRuntimeDefaults = {
|
|
@@ -44,26 +81,309 @@ export const defaultFunnelRuntimeDefaults = {
|
|
|
44
81
|
funnelId: null,
|
|
45
82
|
sdkPublishableKey: null,
|
|
46
83
|
};
|
|
47
|
-
|
|
84
|
+
const asRecord = (value) => {
|
|
85
|
+
return typeof value === 'object' && value !== null ? value : {};
|
|
86
|
+
};
|
|
87
|
+
const str = (value, fallback) => {
|
|
88
|
+
return typeof value === 'string' ? value : fallback;
|
|
89
|
+
};
|
|
90
|
+
const num = (value, fallback) => {
|
|
91
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
92
|
+
};
|
|
93
|
+
const parseLegacyFontFamily = (value, fallback) => {
|
|
94
|
+
if (typeof value !== 'string')
|
|
95
|
+
return fallback;
|
|
96
|
+
const parts = value
|
|
97
|
+
.split(',')
|
|
98
|
+
.map((part) => part.trim())
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
if (parts.length === 0)
|
|
101
|
+
return fallback;
|
|
102
|
+
const family = parts[0].replace(/^['"]|['"]$/g, '');
|
|
103
|
+
const fallbacks = parts.slice(1).join(', ');
|
|
104
|
+
return {
|
|
105
|
+
family: family || fallback.family,
|
|
106
|
+
style: fallback.style,
|
|
107
|
+
fallbacks: fallbacks || fallback.fallbacks,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
export const normalizeFunnelTheme = (theme) => {
|
|
111
|
+
var _a, _b, _c, _d;
|
|
112
|
+
const raw = asRecord(theme);
|
|
113
|
+
const defaults = defaultFunnelTheme;
|
|
114
|
+
const rawColors = asRecord(raw.colors);
|
|
115
|
+
const rawInterface = asRecord(rawColors.interface);
|
|
116
|
+
const rawOpacity = asRecord(rawColors.interfaceOpacity);
|
|
117
|
+
const rawSemantic = asRecord(rawColors.semantic);
|
|
118
|
+
const rawDerived = asRecord(rawColors.derived);
|
|
119
|
+
const rawLegacyColors = rawInterface.background || rawSemantic.accent ? {} : rawColors;
|
|
120
|
+
const rawDarkOverrides = asRecord(raw.darkOverrides);
|
|
121
|
+
const rawDarkInterface = asRecord(rawDarkOverrides.interface);
|
|
122
|
+
const rawDarkSemantic = asRecord(rawDarkOverrides.semantic);
|
|
123
|
+
const rawDarkDerived = asRecord(rawDarkOverrides.derived);
|
|
124
|
+
const rawDarkOpacity = asRecord(rawDarkOverrides.interfaceOpacity);
|
|
125
|
+
const rawTypo = asRecord(raw.typography);
|
|
126
|
+
const rawFont = asRecord(rawTypo.font);
|
|
127
|
+
const rawLegacyFont = parseLegacyFontFamily(rawTypo.fontFamily, defaults.typography.font);
|
|
128
|
+
const rawSizes = asRecord(rawTypo.fontSizes);
|
|
129
|
+
const rawWeights = asRecord(rawTypo.fontWeights);
|
|
130
|
+
const rawLineHeights = asRecord(rawTypo.lineHeights);
|
|
131
|
+
const rawDevice = asRecord(raw.deviceSize);
|
|
132
|
+
const rawPresets = Array.isArray(rawDevice.presets) ? rawDevice.presets : [];
|
|
133
|
+
const rawLayout = asRecord(raw.layout);
|
|
134
|
+
const rawSafe = asRecord(rawLayout.safeAreaOffsets);
|
|
135
|
+
const hasLegacyDevicePreset = typeof rawLayout.shellWidth === 'number' || typeof rawLayout.maxWidth === 'number';
|
|
136
|
+
const darkOverrides = {};
|
|
137
|
+
if (Object.keys(rawDarkInterface).length > 0) {
|
|
138
|
+
darkOverrides.interface = {};
|
|
139
|
+
if (typeof rawDarkInterface.background === 'string') {
|
|
140
|
+
darkOverrides.interface.background = rawDarkInterface.background;
|
|
141
|
+
}
|
|
142
|
+
if (typeof rawDarkInterface.primary === 'string') {
|
|
143
|
+
darkOverrides.interface.primary = rawDarkInterface.primary;
|
|
144
|
+
}
|
|
145
|
+
if (typeof rawDarkInterface.primaryText === 'string') {
|
|
146
|
+
darkOverrides.interface.primaryText = rawDarkInterface.primaryText;
|
|
147
|
+
}
|
|
148
|
+
if (typeof rawDarkInterface.secondary === 'string') {
|
|
149
|
+
darkOverrides.interface.secondary = rawDarkInterface.secondary;
|
|
150
|
+
}
|
|
151
|
+
if (typeof rawDarkInterface.secondaryText === 'string') {
|
|
152
|
+
darkOverrides.interface.secondaryText = rawDarkInterface.secondaryText;
|
|
153
|
+
}
|
|
154
|
+
if (typeof rawDarkInterface.text === 'string') {
|
|
155
|
+
darkOverrides.interface.text = rawDarkInterface.text;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (Object.keys(rawDarkSemantic).length > 0) {
|
|
159
|
+
darkOverrides.semantic = {};
|
|
160
|
+
for (const key of Object.keys(rawDarkSemantic)) {
|
|
161
|
+
if (typeof rawDarkSemantic[key] === 'string') {
|
|
162
|
+
darkOverrides.semantic[key] = rawDarkSemantic[key];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (Object.keys(rawDarkDerived).length > 0) {
|
|
167
|
+
darkOverrides.derived = {};
|
|
168
|
+
for (const key of Object.keys(rawDarkDerived)) {
|
|
169
|
+
if (typeof rawDarkDerived[key] === 'string') {
|
|
170
|
+
darkOverrides.derived[key] = rawDarkDerived[key];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (Object.keys(rawDarkOpacity).length > 0) {
|
|
175
|
+
darkOverrides.interfaceOpacity = {};
|
|
176
|
+
if (typeof rawDarkOpacity.background === 'number') {
|
|
177
|
+
darkOverrides.interfaceOpacity.background = rawDarkOpacity.background;
|
|
178
|
+
}
|
|
179
|
+
if (typeof rawDarkOpacity.primary === 'number') {
|
|
180
|
+
darkOverrides.interfaceOpacity.primary = rawDarkOpacity.primary;
|
|
181
|
+
}
|
|
182
|
+
if (typeof rawDarkOpacity.primaryText === 'number') {
|
|
183
|
+
darkOverrides.interfaceOpacity.primaryText = rawDarkOpacity.primaryText;
|
|
184
|
+
}
|
|
185
|
+
if (typeof rawDarkOpacity.secondary === 'number') {
|
|
186
|
+
darkOverrides.interfaceOpacity.secondary = rawDarkOpacity.secondary;
|
|
187
|
+
}
|
|
188
|
+
if (typeof rawDarkOpacity.secondaryText === 'number') {
|
|
189
|
+
darkOverrides.interfaceOpacity.secondaryText = rawDarkOpacity.secondaryText;
|
|
190
|
+
}
|
|
191
|
+
if (typeof rawDarkOpacity.text === 'number') {
|
|
192
|
+
darkOverrides.interfaceOpacity.text = rawDarkOpacity.text;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
interfaceMode: str(raw.interfaceMode, defaults.interfaceMode),
|
|
197
|
+
colors: {
|
|
198
|
+
interface: {
|
|
199
|
+
background: str(rawInterface.background, str(rawLegacyColors.background, defaults.colors.interface.background)),
|
|
200
|
+
primary: str(rawInterface.primary, str(rawLegacyColors.primary, str(rawLegacyColors.accent, defaults.colors.interface.primary))),
|
|
201
|
+
primaryText: str(rawInterface.primaryText, str(rawLegacyColors.primaryText, str(rawLegacyColors.buttonText, defaults.colors.interface.primaryText))),
|
|
202
|
+
secondary: str(rawInterface.secondary, str(rawLegacyColors.secondary, str(rawLegacyColors.accent, defaults.colors.interface.secondary))),
|
|
203
|
+
secondaryText: str(rawInterface.secondaryText, str(rawLegacyColors.secondaryText, str(rawLegacyColors.text, defaults.colors.interface.secondaryText))),
|
|
204
|
+
text: str(rawInterface.text, str(rawLegacyColors.text, defaults.colors.interface.text)),
|
|
205
|
+
},
|
|
206
|
+
interfaceOpacity: {
|
|
207
|
+
background: num(rawOpacity.background, defaults.colors.interfaceOpacity.background),
|
|
208
|
+
primary: num(rawOpacity.primary, defaults.colors.interfaceOpacity.primary),
|
|
209
|
+
primaryText: num(rawOpacity.primaryText, defaults.colors.interfaceOpacity.primaryText),
|
|
210
|
+
secondary: num(rawOpacity.secondary, defaults.colors.interfaceOpacity.secondary),
|
|
211
|
+
secondaryText: num(rawOpacity.secondaryText, defaults.colors.interfaceOpacity.secondaryText),
|
|
212
|
+
text: num(rawOpacity.text, defaults.colors.interfaceOpacity.text),
|
|
213
|
+
},
|
|
214
|
+
semantic: {
|
|
215
|
+
accent: str(rawSemantic.accent, str(rawLegacyColors.accent, str(rawLegacyColors.primary, defaults.colors.semantic.accent))),
|
|
216
|
+
button: str(rawSemantic.button, str(rawLegacyColors.button, defaults.colors.semantic.button)),
|
|
217
|
+
buttonText: str(rawSemantic.buttonText, str(rawLegacyColors.buttonText, defaults.colors.semantic.buttonText)),
|
|
218
|
+
surface: str(rawSemantic.surface, str(rawLegacyColors.surface, defaults.colors.semantic.surface)),
|
|
219
|
+
border: str(rawSemantic.border, str(rawLegacyColors.border, defaults.colors.semantic.border)),
|
|
220
|
+
warning: str(rawSemantic.warning, str(rawLegacyColors.warning, defaults.colors.semantic.warning)),
|
|
221
|
+
danger: str(rawSemantic.danger, str(rawLegacyColors.danger, defaults.colors.semantic.danger)),
|
|
222
|
+
},
|
|
223
|
+
derived: {
|
|
224
|
+
bgMuted: str(rawDerived.bgMuted, defaults.colors.derived.bgMuted),
|
|
225
|
+
bgSuccess: str(rawDerived.bgSuccess, defaults.colors.derived.bgSuccess),
|
|
226
|
+
bgWarning: str(rawDerived.bgWarning, defaults.colors.derived.bgWarning),
|
|
227
|
+
bgDanger: str(rawDerived.bgDanger, defaults.colors.derived.bgDanger),
|
|
228
|
+
bgNeutral: str(rawDerived.bgNeutral, defaults.colors.derived.bgNeutral),
|
|
229
|
+
textMuted: str(rawDerived.textMuted, defaults.colors.derived.textMuted),
|
|
230
|
+
accentSoft: str(rawDerived.accentSoft, defaults.colors.derived.accentSoft),
|
|
231
|
+
accentMuted: str(rawDerived.accentMuted, defaults.colors.derived.accentMuted),
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
darkOverrides,
|
|
235
|
+
typography: {
|
|
236
|
+
font: {
|
|
237
|
+
family: str(rawFont.family, rawLegacyFont.family),
|
|
238
|
+
style: str(rawFont.style, rawLegacyFont.style),
|
|
239
|
+
fallbacks: str(rawFont.fallbacks, rawLegacyFont.fallbacks),
|
|
240
|
+
},
|
|
241
|
+
fontSizes: {
|
|
242
|
+
xs: str(rawSizes.xs, defaults.typography.fontSizes.xs),
|
|
243
|
+
sm: str(rawSizes.sm, defaults.typography.fontSizes.sm),
|
|
244
|
+
md: str(rawSizes.md, defaults.typography.fontSizes.md),
|
|
245
|
+
lg: str(rawSizes.lg, defaults.typography.fontSizes.lg),
|
|
246
|
+
xl: str(rawSizes.xl, defaults.typography.fontSizes.xl),
|
|
247
|
+
xxl: str(rawSizes.xxl, defaults.typography.fontSizes.xxl),
|
|
248
|
+
},
|
|
249
|
+
fontWeights: {
|
|
250
|
+
regular: num(rawWeights.regular, defaults.typography.fontWeights.regular),
|
|
251
|
+
medium: num(rawWeights.medium, defaults.typography.fontWeights.medium),
|
|
252
|
+
semibold: num(rawWeights.semibold, defaults.typography.fontWeights.semibold),
|
|
253
|
+
bold: num(rawWeights.bold, defaults.typography.fontWeights.bold),
|
|
254
|
+
heavy: num(rawWeights.heavy, defaults.typography.fontWeights.heavy),
|
|
255
|
+
},
|
|
256
|
+
lineHeights: {
|
|
257
|
+
tight: num(rawLineHeights.tight, defaults.typography.lineHeights.tight),
|
|
258
|
+
normal: num(rawLineHeights.normal, defaults.typography.lineHeights.normal),
|
|
259
|
+
relaxed: num(rawLineHeights.relaxed, defaults.typography.lineHeights.relaxed),
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
deviceSize: {
|
|
263
|
+
activePreset: str(rawDevice.activePreset, defaults.deviceSize.activePreset),
|
|
264
|
+
presets: rawPresets.length > 0
|
|
265
|
+
? rawPresets.map((preset) => {
|
|
266
|
+
const record = asRecord(preset);
|
|
267
|
+
return {
|
|
268
|
+
name: str(record.name, 'Universal'),
|
|
269
|
+
shellWidth: num(record.shellWidth, 430),
|
|
270
|
+
maxWidth: num(record.maxWidth, 430),
|
|
271
|
+
};
|
|
272
|
+
})
|
|
273
|
+
: hasLegacyDevicePreset
|
|
274
|
+
? [
|
|
275
|
+
{
|
|
276
|
+
name: defaults.deviceSize.activePreset,
|
|
277
|
+
shellWidth: num(rawLayout.shellWidth, (_b = (_a = defaults.deviceSize.presets[0]) === null || _a === void 0 ? void 0 : _a.shellWidth) !== null && _b !== void 0 ? _b : 430),
|
|
278
|
+
maxWidth: num(rawLayout.maxWidth, (_d = (_c = defaults.deviceSize.presets[0]) === null || _c === void 0 ? void 0 : _c.maxWidth) !== null && _d !== void 0 ? _d : 430),
|
|
279
|
+
},
|
|
280
|
+
]
|
|
281
|
+
: defaults.deviceSize.presets,
|
|
282
|
+
padding: num(rawDevice.padding, defaults.deviceSize.padding),
|
|
283
|
+
cornerRadius: num(rawDevice.cornerRadius, defaults.deviceSize.cornerRadius),
|
|
284
|
+
},
|
|
285
|
+
layout: {
|
|
286
|
+
safeAreaOffsets: {
|
|
287
|
+
top: num(rawSafe.top, defaults.layout.safeAreaOffsets.top),
|
|
288
|
+
right: num(rawSafe.right, defaults.layout.safeAreaOffsets.right),
|
|
289
|
+
bottom: num(rawSafe.bottom, defaults.layout.safeAreaOffsets.bottom),
|
|
290
|
+
left: num(rawSafe.left, defaults.layout.safeAreaOffsets.left),
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
// --- Color resolution for light/dark ---
|
|
296
|
+
export const resolveColors = (theme, mode) => {
|
|
297
|
+
const normalizedTheme = normalizeFunnelTheme(theme);
|
|
298
|
+
if (mode === 'light')
|
|
299
|
+
return normalizedTheme.colors;
|
|
300
|
+
return {
|
|
301
|
+
interface: Object.assign(Object.assign({}, normalizedTheme.colors.interface), normalizedTheme.darkOverrides.interface),
|
|
302
|
+
interfaceOpacity: Object.assign(Object.assign({}, normalizedTheme.colors.interfaceOpacity), normalizedTheme.darkOverrides.interfaceOpacity),
|
|
303
|
+
semantic: Object.assign(Object.assign({}, normalizedTheme.colors.semantic), normalizedTheme.darkOverrides.semantic),
|
|
304
|
+
derived: Object.assign(Object.assign({}, normalizedTheme.colors.derived), normalizedTheme.darkOverrides.derived),
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
// --- Opacity helpers ---
|
|
308
|
+
const hexToRgb = (hex) => {
|
|
309
|
+
const cleaned = hex.replace('#', '');
|
|
310
|
+
if (cleaned.length !== 6)
|
|
311
|
+
return null;
|
|
312
|
+
const value = parseInt(cleaned, 16);
|
|
313
|
+
if (Number.isNaN(value))
|
|
314
|
+
return null;
|
|
315
|
+
return { r: (value >> 16) & 255, g: (value >> 8) & 255, b: value & 255 };
|
|
316
|
+
};
|
|
317
|
+
const applyOpacity = (color, opacity) => {
|
|
318
|
+
if (opacity >= 100)
|
|
319
|
+
return color;
|
|
320
|
+
const rgb = hexToRgb(color);
|
|
321
|
+
if (!rgb)
|
|
322
|
+
return color;
|
|
323
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${(opacity / 100).toFixed(2)})`;
|
|
324
|
+
};
|
|
325
|
+
// --- Active preset resolution ---
|
|
326
|
+
const resolveActivePreset = (deviceSize) => {
|
|
327
|
+
var _a, _b;
|
|
328
|
+
return ((_b = (_a = deviceSize.presets.find((p) => p.name === deviceSize.activePreset)) !== null && _a !== void 0 ? _a : deviceSize.presets[0]) !== null && _b !== void 0 ? _b : { name: 'Universal', shellWidth: 430, maxWidth: 430 });
|
|
329
|
+
};
|
|
330
|
+
// --- CSS variable generation ---
|
|
331
|
+
export const createThemeCssVariables = (theme, mode) => {
|
|
332
|
+
const normalizedTheme = normalizeFunnelTheme(theme);
|
|
333
|
+
const effectiveMode = mode !== null && mode !== void 0 ? mode : normalizedTheme.interfaceMode;
|
|
334
|
+
const colors = resolveColors(normalizedTheme, effectiveMode);
|
|
335
|
+
const preset = resolveActivePreset(normalizedTheme.deviceSize);
|
|
336
|
+
const fontFamily = `"${normalizedTheme.typography.font.family}", ${normalizedTheme.typography.font.fallbacks}`;
|
|
48
337
|
return {
|
|
49
|
-
'--color-bg':
|
|
50
|
-
'--color-
|
|
51
|
-
'--color-
|
|
52
|
-
'--color-
|
|
53
|
-
'--color-
|
|
54
|
-
'--color-
|
|
55
|
-
'--
|
|
56
|
-
'--
|
|
57
|
-
'--
|
|
58
|
-
'--
|
|
59
|
-
'--
|
|
60
|
-
'--
|
|
61
|
-
'--
|
|
62
|
-
'--
|
|
63
|
-
'--
|
|
64
|
-
'--
|
|
65
|
-
'--
|
|
66
|
-
'--
|
|
67
|
-
'--
|
|
338
|
+
'--color-bg': applyOpacity(colors.interface.background, colors.interfaceOpacity.background),
|
|
339
|
+
'--color-bg-raw': colors.interface.background,
|
|
340
|
+
'--color-bg-opacity': String(colors.interfaceOpacity.background),
|
|
341
|
+
'--color-primary': applyOpacity(colors.interface.primary, colors.interfaceOpacity.primary),
|
|
342
|
+
'--color-primary-raw': colors.interface.primary,
|
|
343
|
+
'--color-primary-opacity': String(colors.interfaceOpacity.primary),
|
|
344
|
+
'--color-primary-text': applyOpacity(colors.interface.primaryText, colors.interfaceOpacity.primaryText),
|
|
345
|
+
'--color-primary-text-raw': colors.interface.primaryText,
|
|
346
|
+
'--color-primary-text-opacity': String(colors.interfaceOpacity.primaryText),
|
|
347
|
+
'--color-secondary': applyOpacity(colors.interface.secondary, colors.interfaceOpacity.secondary),
|
|
348
|
+
'--color-secondary-raw': colors.interface.secondary,
|
|
349
|
+
'--color-secondary-opacity': String(colors.interfaceOpacity.secondary),
|
|
350
|
+
'--color-secondary-text': applyOpacity(colors.interface.secondaryText, colors.interfaceOpacity.secondaryText),
|
|
351
|
+
'--color-secondary-text-raw': colors.interface.secondaryText,
|
|
352
|
+
'--color-secondary-text-opacity': String(colors.interfaceOpacity.secondaryText),
|
|
353
|
+
'--color-text': applyOpacity(colors.interface.text, colors.interfaceOpacity.text),
|
|
354
|
+
'--color-text-raw': colors.interface.text,
|
|
355
|
+
'--color-text-opacity': String(colors.interfaceOpacity.text),
|
|
356
|
+
'--color-accent': colors.semantic.accent,
|
|
357
|
+
'--color-button': colors.semantic.button,
|
|
358
|
+
'--color-button-text': colors.semantic.buttonText,
|
|
359
|
+
'--color-surface': colors.semantic.surface,
|
|
360
|
+
'--color-border': colors.semantic.border,
|
|
361
|
+
'--color-warning': colors.semantic.warning,
|
|
362
|
+
'--color-danger': colors.semantic.danger,
|
|
363
|
+
'--color-bg-muted': colors.derived.bgMuted,
|
|
364
|
+
'--color-bg-success': colors.derived.bgSuccess,
|
|
365
|
+
'--color-bg-warning': colors.derived.bgWarning,
|
|
366
|
+
'--color-bg-danger': colors.derived.bgDanger,
|
|
367
|
+
'--color-bg-neutral': colors.derived.bgNeutral,
|
|
368
|
+
'--color-text-muted': colors.derived.textMuted,
|
|
369
|
+
'--color-accent-soft': colors.derived.accentSoft,
|
|
370
|
+
'--color-accent-muted': colors.derived.accentMuted,
|
|
371
|
+
'--font-family-base': fontFamily,
|
|
372
|
+
'--font-family-display': fontFamily,
|
|
373
|
+
'--font-weight-regular': String(normalizedTheme.typography.fontWeights.regular),
|
|
374
|
+
'--font-weight-medium': String(normalizedTheme.typography.fontWeights.medium),
|
|
375
|
+
'--font-weight-semibold': String(normalizedTheme.typography.fontWeights.semibold),
|
|
376
|
+
'--font-weight-bold': String(normalizedTheme.typography.fontWeights.bold),
|
|
377
|
+
'--font-weight-heavy': String(normalizedTheme.typography.fontWeights.heavy),
|
|
378
|
+
'--line-height-body': String(normalizedTheme.typography.lineHeights.normal),
|
|
379
|
+
'--line-height-heading': String(normalizedTheme.typography.lineHeights.tight),
|
|
380
|
+
'--shell-width': `${preset.shellWidth}px`,
|
|
381
|
+
'--max-width': `${preset.maxWidth}px`,
|
|
382
|
+
'--padding': `${normalizedTheme.deviceSize.padding}px`,
|
|
383
|
+
'--corner-radius': `${normalizedTheme.deviceSize.cornerRadius}px`,
|
|
384
|
+
'--safe-area-offset-top': `${normalizedTheme.layout.safeAreaOffsets.top}px`,
|
|
385
|
+
'--safe-area-offset-right': `${normalizedTheme.layout.safeAreaOffsets.right}px`,
|
|
386
|
+
'--safe-area-offset-bottom': `${normalizedTheme.layout.safeAreaOffsets.bottom}px`,
|
|
387
|
+
'--safe-area-offset-left': `${normalizedTheme.layout.safeAreaOffsets.left}px`,
|
|
68
388
|
};
|
|
69
389
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FunnelStepKind, FunnelStepType } from '../steps/types';
|
|
1
|
+
import type { FunnelStepKind, FunnelStepType } from '../steps/types.js';
|
|
2
2
|
export type TemplateArchitectureVersion = number;
|
|
3
3
|
export type FunnelManifestStep = {
|
|
4
4
|
id: string;
|
|
@@ -9,7 +9,16 @@ export type FunnelManifestStep = {
|
|
|
9
9
|
kind?: FunnelStepKind;
|
|
10
10
|
name?: string;
|
|
11
11
|
title: string;
|
|
12
|
+
assetIds?: readonly string[];
|
|
12
13
|
};
|
|
14
|
+
export type FunnelManifestImageAsset = {
|
|
15
|
+
type: 'image';
|
|
16
|
+
src: string;
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
preload?: 'idle' | false;
|
|
20
|
+
};
|
|
21
|
+
export type FunnelManifestAsset = string | FunnelManifestImageAsset;
|
|
13
22
|
export type FunnelManifestEntryPoint = {
|
|
14
23
|
id: string;
|
|
15
24
|
stepId: string;
|
|
@@ -20,14 +29,11 @@ export type FunnelManifestEdge = {
|
|
|
20
29
|
conditionId?: string;
|
|
21
30
|
};
|
|
22
31
|
export type FunnelManifestExperimentVariant = {
|
|
23
|
-
|
|
24
|
-
label: string;
|
|
25
|
-
trafficPercent: number;
|
|
32
|
+
variantKey: string;
|
|
26
33
|
routeToStepId: string;
|
|
27
34
|
};
|
|
28
35
|
export type FunnelManifestExperiment = {
|
|
29
36
|
experimentId: string;
|
|
30
|
-
status: 'draft' | 'active';
|
|
31
37
|
stepId: string;
|
|
32
38
|
variants: readonly FunnelManifestExperimentVariant[];
|
|
33
39
|
};
|
|
@@ -41,9 +47,9 @@ export type FunnelManifest = {
|
|
|
41
47
|
width: number;
|
|
42
48
|
height: number;
|
|
43
49
|
};
|
|
44
|
-
assets: Record<string,
|
|
50
|
+
assets: Record<string, FunnelManifestAsset>;
|
|
45
51
|
steps: readonly FunnelManifestStep[];
|
|
46
|
-
entryPoints
|
|
52
|
+
entryPoints?: readonly FunnelManifestEntryPoint[];
|
|
47
53
|
edgesByStepId: Partial<Record<string, readonly FunnelManifestEdge[]>>;
|
|
48
54
|
experiments: readonly FunnelManifestExperiment[];
|
|
49
55
|
};
|