@pushwoosh/websdk-common 0.0.0 → 6.16.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.
@@ -0,0 +1,71 @@
1
+ import { type CSSProperties } from 'react';
2
+ import { type WebPopupButtonVariant, type WebPopupColors, type WebPopupRatingScale, type WebPopupResolvedColors, type WebPopupSlot, type WebPopupTextVariant } from './types';
3
+ /**
4
+ * Current stored-format version (WebPopupJson.version). Version 2 = the
5
+ * layout-tree format (nested containers + Content/Image leaves, named content
6
+ * zones). Version 1 (flat layout + single slot list) is retired without a
7
+ * migration path — v1 popups are treated as broken, same as the pre-engine
8
+ * ai-builder format.
9
+ */
10
+ export declare const WEB_POPUP_VERSION = 2;
11
+ export declare const WEB_POPUP_LAYOUT_MAX_DEPTH = 10;
12
+ export declare const WEB_POPUP_LAYOUT_MAX_NODES = 100;
13
+ /** Protocols allowed in button hrefs (plus relative `#`/`/`/`?` prefixes). */
14
+ export declare const SAFE_URL_PROTOCOL: RegExp;
15
+ export declare const WEB_POPUP_FONT_STACK = "Inter, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif";
16
+ export declare const WEB_POPUP_TEXT_COLOR = "#101828";
17
+ export declare const WEB_POPUP_TEXT_SECONDARY_COLOR = "#667085";
18
+ export declare const WEB_POPUP_ACCENT_COLOR = "#2970FF";
19
+ export declare const WEB_POPUP_BACKGROUND_COLOR = "#FFFFFF";
20
+ /** Label over an accent fill in the light scheme; dark picks it by accent luminance. */
21
+ export declare const WEB_POPUP_ON_ACCENT_COLOR = "#FFFFFF";
22
+ export declare const WEB_POPUP_DEFAULT_COLORS: WebPopupColors;
23
+ /** Fallback width used when a fixed-width context needs a number anyway. */
24
+ export declare const WEB_POPUP_DEFAULT_WIDTH = 360;
25
+ export declare const WEB_POPUP_TEXT_VARIANT_STYLES: Record<WebPopupTextVariant, CSSProperties>;
26
+ /**
27
+ * A text slot's color: the palette's main/secondary role by variant; in an
28
+ * 'inverse' zone (over a background image) — colors.textInverse, with the
29
+ * secondary variants toned translucent. Undefined for a non-inverse 'icon'
30
+ * (an emoji line inherits — its glyphs aren't recolorable anyway).
31
+ */
32
+ export declare function webPopupTextColor(variant: WebPopupTextVariant, colors: WebPopupColors, inverse: boolean): string | undefined;
33
+ /** The dark fill under a container's background image while it loads. */
34
+ export declare const WEB_POPUP_BG_SECTION_FALLBACK = "#1D2939";
35
+ export type WebPopupSlotRole = 'heading' | 'text' | 'action';
36
+ export declare function webPopupSlotRole(slot: WebPopupSlot): WebPopupSlotRole;
37
+ export declare function webPopupSlotGap(prev: WebPopupSlotRole, next: WebPopupSlotRole): number;
38
+ export declare const WEB_POPUP_TEXT_SLOT_PADDING = "4px 0";
39
+ export declare const WEB_POPUP_BUTTON_SLOT_PADDING = "8px 0 4px";
40
+ /**
41
+ * The button pill both button slots and a form slot's submit button render
42
+ * with — buttons pair the accent with white (primary = accent fill + white
43
+ * label, secondary = white fill + accent outline). Shared with the editor's
44
+ * canvas (parity contract).
45
+ */
46
+ export declare function webPopupButtonPillStyle(variant: WebPopupButtonVariant, colors: WebPopupResolvedColors): CSSProperties;
47
+ export declare const WEB_POPUP_FORM_INPUT_BORDER = "#D0D5DD";
48
+ export declare const WEB_POPUP_FORM_ERROR_COLOR = "#D92D20";
49
+ /** The error red lifted for a dark background — the same Untitled UI ramp, three steps up. */
50
+ export declare const WEB_POPUP_FORM_ERROR_COLOR_DARK = "#FDA29B";
51
+ export declare function webPopupFormInputStyle(colors: WebPopupResolvedColors, borderColor?: string): CSSProperties;
52
+ export declare const WEB_POPUP_FORM_DEFAULTS: {
53
+ readonly submitLabel: "Subscribe";
54
+ readonly successMessage: "Thanks for subscribing!";
55
+ readonly confirmEmailMessage: "Please check your inbox and confirm your email.";
56
+ };
57
+ export declare const WEB_POPUP_CHOICE_DEFAULTS: {
58
+ readonly submitLabel: "Save";
59
+ readonly successMessage: "Thanks! Your choice is saved.";
60
+ };
61
+ export declare const WEB_POPUP_RATING_DEFAULTS: {
62
+ readonly submitLabel: "Send";
63
+ readonly successMessage: "Thanks for the feedback!";
64
+ };
65
+ export declare function webPopupRatingScores(scale: WebPopupRatingScale): number[];
66
+ /** Options a `choice` slot may carry; exported so the editor can't offer one normalization drops. */
67
+ export declare const WEB_POPUP_MAX_CHOICE_OPTIONS = 12;
68
+ /** What a slot says when the host callback rejected: nothing actionable, so it's retryable. */
69
+ export declare const WEB_POPUP_RETRY_ERROR = "Something went wrong. Please try again.";
70
+ export declare const WEB_POPUP_KEYFRAMES = "\n@keyframes pw-vpopup-fade { from { opacity: 0; } }\n@keyframes pw-vpopup-slide { from { opacity: 0; translate: 0 28px; } }\n@keyframes pw-vpopup-overlay { from { opacity: 0; } }\n@keyframes pw-vpopup-page { from { opacity: 0; } }\n";
71
+ export declare const WEB_POPUP_PAGE_TRANSITION_MS = 300;
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Current stored-format version (WebPopupJson.version). Version 2 = the
3
+ * layout-tree format (nested containers + Content/Image leaves, named content
4
+ * zones). Version 1 (flat layout + single slot list) is retired without a
5
+ * migration path — v1 popups are treated as broken, same as the pre-engine
6
+ * ai-builder format.
7
+ */
8
+ export const WEB_POPUP_VERSION = 2;
9
+ // Bomb protection for the embedded layout tree — the json travels through a
10
+ // public API, so the parser refuses to walk pathological trees.
11
+ export const WEB_POPUP_LAYOUT_MAX_DEPTH = 10;
12
+ export const WEB_POPUP_LAYOUT_MAX_NODES = 100;
13
+ /** Protocols allowed in button hrefs (plus relative `#`/`/`/`?` prefixes). */
14
+ export const SAFE_URL_PROTOCOL = /^(https?:|mailto:|tel:)/i;
15
+ export const WEB_POPUP_FONT_STACK = 'Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
16
+ // Palette defaults (mirror the editor's popup theme) — what a popup gets for
17
+ // any WebPopupColors entry its json doesn't set.
18
+ export const WEB_POPUP_TEXT_COLOR = '#101828';
19
+ export const WEB_POPUP_TEXT_SECONDARY_COLOR = '#667085';
20
+ export const WEB_POPUP_ACCENT_COLOR = '#2970FF';
21
+ export const WEB_POPUP_BACKGROUND_COLOR = '#FFFFFF';
22
+ /** Label over an accent fill in the light scheme; dark picks it by accent luminance. */
23
+ export const WEB_POPUP_ON_ACCENT_COLOR = '#FFFFFF';
24
+ export const WEB_POPUP_DEFAULT_COLORS = {
25
+ background: WEB_POPUP_BACKGROUND_COLOR,
26
+ accent: WEB_POPUP_ACCENT_COLOR,
27
+ text: WEB_POPUP_TEXT_COLOR,
28
+ textSecondary: WEB_POPUP_TEXT_SECONDARY_COLOR,
29
+ textInverse: '#FFFFFF'
30
+ };
31
+ /** Fallback width used when a fixed-width context needs a number anyway. */
32
+ export const WEB_POPUP_DEFAULT_WIDTH = 360;
33
+ // Per-variant text styles — the same numbers the editor's canvas derives from
34
+ // its theme presets. No colors here: those come from the popup palette via
35
+ // webPopupTextColor below.
36
+ export const WEB_POPUP_TEXT_VARIANT_STYLES = {
37
+ h1: {
38
+ fontSize: 24,
39
+ fontWeight: 600,
40
+ lineHeight: 1.25
41
+ },
42
+ h2: {
43
+ fontSize: 18,
44
+ fontWeight: 600,
45
+ lineHeight: 1.3
46
+ },
47
+ h3: {
48
+ fontSize: 16,
49
+ fontWeight: 600,
50
+ lineHeight: 1.35
51
+ },
52
+ body: {
53
+ fontSize: 14,
54
+ fontWeight: 'normal',
55
+ lineHeight: 1.5
56
+ },
57
+ caption: {
58
+ fontSize: 12,
59
+ fontWeight: 'normal',
60
+ lineHeight: 1.4
61
+ },
62
+ eyebrow: {
63
+ fontSize: 11,
64
+ fontWeight: 600,
65
+ letterSpacing: '0.08em',
66
+ lineHeight: 1.4
67
+ },
68
+ icon: {
69
+ fontSize: 24,
70
+ lineHeight: 1.2
71
+ }
72
+ };
73
+ // Inverse-tone translucency per text style: headings full-strength, the
74
+ // secondary variants toned down — same rhythm the palette's default white
75
+ // set always had.
76
+ const INVERSE_TEXT_ALPHAS = {
77
+ h1: 1,
78
+ h2: 1,
79
+ h3: 1,
80
+ icon: 1,
81
+ eyebrow: 0.8,
82
+ body: 0.9,
83
+ caption: 0.75
84
+ };
85
+ // colors.textInverse at the given opacity. Only #rgb/#rrggbb can be blended;
86
+ // any other author-supplied css color renders full-strength rather than
87
+ // being guessed at.
88
+ function inverseColorAt(color, alpha) {
89
+ if (alpha >= 1) return color;
90
+ const hex = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(color)?.[1];
91
+ if (!hex) return color;
92
+ const full = hex.length === 3 ? hex.split('').map(c => c + c).join('') : hex;
93
+ const [r, g, b] = [0, 2, 4].map(i => parseInt(full.slice(i, i + 2), 16));
94
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
95
+ }
96
+ /**
97
+ * A text slot's color: the palette's main/secondary role by variant; in an
98
+ * 'inverse' zone (over a background image) — colors.textInverse, with the
99
+ * secondary variants toned translucent. Undefined for a non-inverse 'icon'
100
+ * (an emoji line inherits — its glyphs aren't recolorable anyway).
101
+ */
102
+ export function webPopupTextColor(variant, colors, inverse) {
103
+ if (inverse) return inverseColorAt(colors.textInverse, INVERSE_TEXT_ALPHAS[variant] ?? 1);
104
+ if (variant === 'icon') return undefined;
105
+ return variant === 'caption' || variant === 'eyebrow' ? colors.textSecondary : colors.text;
106
+ }
107
+ /** The dark fill under a container's background image while it loads. */
108
+ export const WEB_POPUP_BG_SECTION_FALLBACK = '#1D2939';
109
+ const HEADING_VARIANTS = ['h1', 'h2', 'h3', 'icon'];
110
+ export function webPopupSlotRole(slot) {
111
+ // An icon reads as the line above a title, so it hugs what follows; a
112
+ // countdown wants a block's air, which the 'action' fallback gives it.
113
+ if (slot.type === 'glyph') return 'heading';
114
+ if (slot.type !== 'text') return 'action';
115
+ return HEADING_VARIANTS.includes(slot.variant) ? 'heading' : 'text';
116
+ }
117
+ export function webPopupSlotGap(prev, next) {
118
+ if (next === 'action') return prev === 'action' ? 6 : 16;
119
+ if (next === 'text' && prev === 'heading') return 4;
120
+ return 8;
121
+ }
122
+ // Paddings INSIDE a form slot (field rows, submit pill); the rhythm above
123
+ // owns the space BETWEEN slots.
124
+ export const WEB_POPUP_TEXT_SLOT_PADDING = '4px 0';
125
+ export const WEB_POPUP_BUTTON_SLOT_PADDING = '8px 0 4px';
126
+ /**
127
+ * The button pill both button slots and a form slot's submit button render
128
+ * with — buttons pair the accent with white (primary = accent fill + white
129
+ * label, secondary = white fill + accent outline). Shared with the editor's
130
+ * canvas (parity contract).
131
+ */
132
+ export function webPopupButtonPillStyle(variant, colors) {
133
+ return {
134
+ display: 'block',
135
+ boxSizing: 'border-box',
136
+ width: '100%',
137
+ padding: '10px 25px',
138
+ borderRadius: 6,
139
+ fontSize: 14,
140
+ fontWeight: 600,
141
+ fontFamily: 'inherit',
142
+ lineHeight: 1.5,
143
+ textAlign: 'center',
144
+ textDecoration: 'none',
145
+ cursor: 'pointer',
146
+ ...(variant === 'secondary' ? {
147
+ backgroundColor: colors.surface,
148
+ color: colors.accent,
149
+ border: `1px solid ${colors.accent}`
150
+ } : {
151
+ backgroundColor: colors.accent,
152
+ color: colors.onAccent,
153
+ border: 'none'
154
+ })
155
+ };
156
+ }
157
+ // Form-slot input chrome, shared with the editor's canvas (parity contract):
158
+ // a `surface` card whatever the zone tone, only the border varies.
159
+ export const WEB_POPUP_FORM_INPUT_BORDER = '#D0D5DD';
160
+ export const WEB_POPUP_FORM_ERROR_COLOR = '#D92D20';
161
+ /** The error red lifted for a dark background — the same Untitled UI ramp, three steps up. */
162
+ export const WEB_POPUP_FORM_ERROR_COLOR_DARK = '#FDA29B';
163
+ export function webPopupFormInputStyle(colors, borderColor = colors.border) {
164
+ return {
165
+ display: 'block',
166
+ boxSizing: 'border-box',
167
+ width: '100%',
168
+ padding: '10px 12px',
169
+ borderRadius: 6,
170
+ fontSize: 14,
171
+ fontFamily: 'inherit',
172
+ lineHeight: 1.5,
173
+ backgroundColor: colors.surface,
174
+ color: colors.surfaceText,
175
+ border: `1px solid ${borderColor}`,
176
+ outline: 'none'
177
+ };
178
+ }
179
+ // What a normalized form slot falls back to when the json omits the copy —
180
+ // also the editor's defaults for a freshly added slot.
181
+ export const WEB_POPUP_FORM_DEFAULTS = {
182
+ submitLabel: 'Subscribe',
183
+ successMessage: 'Thanks for subscribing!',
184
+ confirmEmailMessage: 'Please check your inbox and confirm your email.'
185
+ };
186
+ // What a normalized choice slot falls back to when the json omits the copy.
187
+ export const WEB_POPUP_CHOICE_DEFAULTS = {
188
+ submitLabel: 'Save',
189
+ successMessage: 'Thanks! Your choice is saved.'
190
+ };
191
+ // Same, for a rating slot.
192
+ export const WEB_POPUP_RATING_DEFAULTS = {
193
+ submitLabel: 'Send',
194
+ successMessage: 'Thanks for the feedback!'
195
+ };
196
+ // The scores a scale offers, in display order; thumbs are 0/1 so `score` stays
197
+ // "higher is better" everywhere. Shared with the editor canvas (parity).
198
+ export function webPopupRatingScores(scale) {
199
+ if (scale === 'nps') return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
200
+ if (scale === 'stars') return [1, 2, 3, 4, 5];
201
+ return [0, 1];
202
+ }
203
+ /** Options a `choice` slot may carry; exported so the editor can't offer one normalization drops. */
204
+ export const WEB_POPUP_MAX_CHOICE_OPTIONS = 12;
205
+ /** What a slot says when the host callback rejected: nothing actionable, so it's retryable. */
206
+ export const WEB_POPUP_RETRY_ERROR = 'Something went wrong. Please try again.';
207
+ export const WEB_POPUP_KEYFRAMES = `
208
+ @keyframes pw-vpopup-fade { from { opacity: 0; } }
209
+ @keyframes pw-vpopup-slide { from { opacity: 0; translate: 0 28px; } }
210
+ @keyframes pw-vpopup-overlay { from { opacity: 0; } }
211
+ @keyframes pw-vpopup-page { from { opacity: 0; } }
212
+ `;
213
+ // Page-switch transition: the incoming page's content fades in while explicit
214
+ // px sizes glide instead of jumping (auto sizes still snap — 'auto' can't be
215
+ // transitioned). One duration for both so they read as a single move.
216
+ export const WEB_POPUP_PAGE_TRANSITION_MS = 300;
@@ -0,0 +1,8 @@
1
+ import { type WebPopupGlyphName, type WebPopupGlyphSize } from './types';
2
+ export declare const WEB_POPUP_GLYPH_PATHS: Record<WebPopupGlyphName, string>;
3
+ /** The icon names a `glyph` slot may carry — derived, never a second list. */
4
+ export declare const WEB_POPUP_GLYPH_NAMES: WebPopupGlyphName[];
5
+ /** The sizes a glyph slot may take, px; the first is the normalization default. */
6
+ export declare const WEB_POPUP_GLYPH_SIZES: WebPopupGlyphSize[];
7
+ /** The thumb a 'thumbs' rating draws; not author-selectable, so not in the whitelist. */
8
+ export declare const WEB_POPUP_THUMB_UP_PATH: string;
@@ -0,0 +1,30 @@
1
+ // Hand-authored 24×24 stroke geometry, inline (no icon package in a script that
2
+ // loads on the customer's site). EXHAUSTIVE: a name without geometry won't compile.
3
+ export const WEB_POPUP_GLYPH_PATHS = {
4
+ bell: 'M6 10a6 6 0 0 1 12 0c0 4 1.5 5.5 2 6H4c.5-.5 2-2 2-6M10 20a2 2 0 0 0 4 0',
5
+ gift: 'M3 11h18v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1zM2.5 7h19v4h-19zM12 7v14' + 'M12 7c-1 0-3-.2-3.5-1a2 2 0 0 1 3.5-1 2 2 0 0 1 3.5 1c-.5.8-2.5 1-3.5 1z',
6
+ cart: 'M2 4h2.2l2.4 11.2A2 2 0 0 0 8.6 17H19M6 7h15l-2.2 7.5H8' + 'M10.5 20a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0M19.5 20a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0',
7
+ chart: 'M3 21h18M7 21V11M12 21V6M17 21v-8',
8
+ lock: 'M5 11h14v10H5zM8 11V8a4 4 0 0 1 8 0v3',
9
+ calendar: 'M4 6h16v15H4zM4 10h16M9 3v4M15 3v4',
10
+ check: 'M4 13l5 5L20 6',
11
+ star: 'M12 3l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5-5.8-3.1-5.8 3.1 1.1-6.5L2.6 9.8l6.5-.9z',
12
+ clock: 'M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0M12 7v5l4 2',
13
+ tag: 'M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0l-7-7A2 2 0 0 1 3 12.2V5a2 2 0 0 1 2-2h7.2a2 2 0 0 1 1.4.6' + 'l7 7a2 2 0 0 1 0 2.8zM9 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0',
14
+ mail: 'M3 6h18v12H3zM3 7l9 6 9-6',
15
+ phone: 'M7 2h10a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1M10 19h4',
16
+ download: 'M12 3v12M7 11l5 5 5-5M4 20h16',
17
+ play: 'M8 5l12 7-12 7z',
18
+ shield: 'M12 3l8 3v6c0 5-3.4 8.6-8 10-4.6-1.4-8-5-8-10V6z',
19
+ sparkle: 'M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z' + 'M18 15l.9 2.1 2.1.9-2.1.9-.9 2.1-.9-2.1L15 18l2.1-.9z',
20
+ percent: 'M19 5L5 19M8.5 8.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0M20.5 15.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0',
21
+ truck: 'M2 7h12v10H2zM14 10h4l3 3v4h-7' + 'M8.5 19a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0M20 19a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0',
22
+ ticket: 'M4 7h16v4a2 2 0 0 0 0 4v3H4v-3a2 2 0 0 0 0-4zM14 7v12',
23
+ user: 'M20 21a8 8 0 0 0-16 0M16 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0'
24
+ };
25
+ /** The icon names a `glyph` slot may carry — derived, never a second list. */
26
+ export const WEB_POPUP_GLYPH_NAMES = Object.keys(WEB_POPUP_GLYPH_PATHS);
27
+ /** The sizes a glyph slot may take, px; the first is the normalization default. */
28
+ export const WEB_POPUP_GLYPH_SIZES = [24, 32, 48];
29
+ /** The thumb a 'thumbs' rating draws; not author-selectable, so not in the whitelist. */
30
+ export const WEB_POPUP_THUMB_UP_PATH = 'M8 21V10l3.5-7A2 2 0 0 1 14 5v5h4.5a2 2 0 0 1 2 2.4' + 'l-1.3 6A2 2 0 0 1 17.2 21zM8 10H3.5v11H8';
@@ -0,0 +1,84 @@
1
+ import { type WebPopupColors, type WebPopupDark, type WebPopupJson, type WebPopupParams, type WebPopupPlacement, type WebPopupResolvedPage, type WebPopupValueParam } from './types';
2
+ export declare function safeUrl(url: string | undefined): string | undefined;
3
+ /**
4
+ * The target/rel pair a popup link renders with: only an exact '_self' opts
5
+ * out of the default new tab (and of its noreferrer, which same-tab
6
+ * navigation doesn't need). One rule for both link kinds — the reader's
7
+ * button anchors and the sanitizer's inline text links.
8
+ */
9
+ export declare function webPopupLinkTargetAttrs(target: string | null | undefined): {
10
+ target: '_blank' | '_self';
11
+ rel?: 'noreferrer';
12
+ };
13
+ /**
14
+ * Sanitize a text slot's html before it's injected into the page. The editor
15
+ * only ever writes inline formatting, but the json travels through a public
16
+ * API and renders on customers' sites — so on render only the inline tags
17
+ * survive (anything else is unwrapped to its text), every attribute is
18
+ * dropped, and links keep only a safeUrl-approved href plus their tab choice
19
+ * (target="_self" stays; everything else opens in a new tab).
20
+ */
21
+ export declare function sanitizeWebPopupHtml(html: string): string;
22
+ type LegacyColorFields = {
23
+ background?: unknown;
24
+ accent?: unknown;
25
+ };
26
+ /**
27
+ * The popup palette from API-writable params: `colors` entries when present,
28
+ * the legacy top-level background/accent underneath, palette defaults for the
29
+ * rest — always a complete WebPopupColors.
30
+ */
31
+ export declare function normalizeWebPopupColors(params: Partial<WebPopupParams> & LegacyColorFields): WebPopupColors;
32
+ export declare function normalizeWebPopupDark(params: Partial<WebPopupParams>, light: WebPopupColors): WebPopupDark | undefined;
33
+ export declare function parseWebPopupJson(json: string): WebPopupJson | null;
34
+ /** The editor's save path: model → PopupFormContent.json string. */
35
+ export declare function stringifyWebPopup(params: WebPopupParams): string;
36
+ /**
37
+ * The page's named layout values from API-writable json: capped count,
38
+ * whitelisted keys, short '$'-free value strings (substitution is single-pass
39
+ * and this keeps refs from even *looking* chainable), validated controls.
40
+ */
41
+ export declare function normalizeWebPopupValues(raw: unknown): WebPopupValueParam[];
42
+ /** The values as a substitution map (see substituteWebPopupLayoutValues). */
43
+ export declare function webPopupValuesMap(values: WebPopupValueParam[]): Record<string, string>;
44
+ /**
45
+ * Every subscription form code the popup's form slots reference (unique,
46
+ * empty codes skipped) — what a host records view stats for. Reads the RAW
47
+ * params (tolerant of malformed slots), so it works on parsed-but-unresolved
48
+ * json.
49
+ */
50
+ export declare function collectWebPopupFormCodes(params: WebPopupParams): string[];
51
+ export declare function webPopupExpired(params: WebPopupParams, nowMs: number): boolean;
52
+ /**
53
+ * The popup box-size ceiling px, mirroring the layout tree's px clamp: the
54
+ * json is API-writable and the size lands as inline style on the customer's
55
+ * page. The editor clamps its Width/Height inputs to it too.
56
+ */
57
+ export declare const WEB_POPUP_MAX_SIZE_PX = 2000;
58
+ /**
59
+ * A page's final box dimension: the page's own (null = explicitly auto) over
60
+ * the chrome's, validated — a finite positive number clamped to the ceiling,
61
+ * or the literal 'stretch' (fill the viewport along that axis). 'stretch' is
62
+ * a HEIGHT value: the width axis has no such literal — absent already means
63
+ * stretch — so there an API-written one degrades to that same absence. A
64
+ * malformed page value falls back to the chrome (like an absent one), a
65
+ * malformed chrome value = auto. Exported for the editor — it must resolve
66
+ * and validate exactly like the runtime (parity contract), never fork this.
67
+ */
68
+ export declare function resolveWebPopupPageSize(own: number | 'stretch' | null | undefined, shared: number | 'stretch' | undefined, axis: 'width'): number | undefined;
69
+ export declare function resolveWebPopupPageSize(own: number | 'stretch' | null | undefined, shared: number | 'stretch' | undefined, axis: 'height'): number | 'stretch' | undefined;
70
+ /**
71
+ * The renderable pages: each page carries its own embedded layout tree — no
72
+ * kind catalog at runtime. The page's named values are substituted into the
73
+ * tree first ($key tokens), then the result is validated/normalized
74
+ * (whitelisted props, unknown container types degrade to a Vertical stack) —
75
+ * so substituted values pass the same clamps as literals; pages whose layout
76
+ * isn't a tree at all are dropped. An empty result means the popup can't be
77
+ * rendered. width/height come out FINAL (page override ?? chrome, validated)
78
+ * — the renderer never falls back itself.
79
+ */
80
+ export declare function resolveWebPopupPages(params: WebPopupParams): WebPopupResolvedPage[];
81
+ /** The page shown first: startPage when it exists, else the first page. */
82
+ export declare function startPageOf(params: WebPopupParams, pages: WebPopupResolvedPage[]): WebPopupResolvedPage;
83
+ export declare function placementMaxSize(placement: WebPopupPlacement): string;
84
+ export {};