@mrrocket/sdk 0.14.0 → 0.15.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/README.md +9 -3
- package/dist/api-client.d.ts +62 -1
- package/dist/api-client.js +28 -1
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +2 -2
- package/dist/browser/mrrocket-messenger.js +202 -46
- package/dist/browser/mrrocket.js +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/flows-ui.js +48 -1
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows.d.ts +51 -1
- package/dist/flows.js +62 -21
- package/dist/flows.js.map +1 -1
- package/dist/messenger-ui.d.ts +19 -6
- package/dist/messenger-ui.js +332 -78
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +11 -3
- package/dist/messenger.js +6 -3
- package/dist/messenger.js.map +1 -1
- package/dist/react-native/index.d.cts +102 -6
- package/dist/react-native/index.d.ts +102 -6
- package/dist/react-native/index.js +447 -181
- package/dist/react-native/index.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +200 -44
- package/dist/styles-css.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -406,6 +406,8 @@ const cancelFlow = useCancelFlow();
|
|
|
406
406
|
const result = await cancelFlow.open({
|
|
407
407
|
customer: account.isBusiness ? "business" : "consumer",
|
|
408
408
|
price: { amount: 29, currency: "EUR" }, // optional: shows what the discount costs
|
|
409
|
+
plan: account.planId, // optional (0.15.0): for offers by plan, your billing's plan id or name
|
|
410
|
+
customerSince: account.paidSince, // optional (0.15.0): for offers by how long they have paid
|
|
409
411
|
onCancel: (r) => api.cancelSubscription({ reason: r.reason, comment: r.comment }),
|
|
410
412
|
onSave: (r) => api.applyOffer(r.offer), // { kind: "discount", percent, months } | { kind: "pause", months } | { kind: "downgrade", plan } | { kind: "contact" }
|
|
411
413
|
});
|
|
@@ -416,6 +418,8 @@ const cancelFlow = useCancelFlow();
|
|
|
416
418
|
</button>;
|
|
417
419
|
```
|
|
418
420
|
|
|
421
|
+
Since 0.15.0 an answer can ask one follow-up question, show a different offer by the follow-up's answer, the plan or how long the customer has paid (the first rule that fits wins; one about the plan or time needs `plan` / `customerSince`), and make a second offer once the first is declined. `result.answer` holds the follow-up's answer.
|
|
422
|
+
|
|
419
423
|
MRRocket never changes a subscription (its payment key is read-only): `onCancel` and `onSave` are your code. Only after they succeed is the outcome recorded, and MRRocket shows it under Churn as a cancellation or a save with the customer's plan and monthly revenue (matched by your `identify()` id, or the email you passed with it). If one throws, the dialog stays open with "Something went wrong. Please try again." Script tag: `mrrocket.cancelFlow.open({ … })`.
|
|
420
424
|
|
|
421
425
|
**Onboarding** (Conversion › Onboarding): the tour, checklist and modal steps of a live flow. A flow starts on its own when the person does its trigger: `track("Signed up")` (or `signup`, `sign_up`) for "Signed up", `track("first_export")` for "First export", and the first page or screen for "First launch" / "First visit", once per person. Automatic starts need a signed-in person (`identify`) or stored progress (`consent="cookies"` / `"persistent"`), so nobody gets the same tour on every page. `useCockpit().startOnboarding()` (script tag `mrrocket.onboarding.start()`, React Native `useOnboarding().start()`) starts one whenever you want; `onboarding={false}` on the provider switches the automatic start off. A tour step points at the element marked `data-ck-target="…"` (web); a checklist item with an event ticks itself when you track it. Emails of the flow go out from MRRocket.
|
|
@@ -483,9 +487,11 @@ const messenger = useMessenger();
|
|
|
483
487
|
<a data-ck-messenger>Chat with us</a>;
|
|
484
488
|
```
|
|
485
489
|
|
|
486
|
-
The look and texts (color, light or dark,
|
|
490
|
+
The look and texts (color, light or dark, corners, Home's top, font, place, name, greeting, button text, Help tab, sound, preview, asking for an email, and "Powered by MRRocket", which Studio and Agency can switch off) are set per product in MRRocket › Support › Messenger and read by the messenger itself (`GET /api/sdk/messenger`), so they change without a deploy. What you pass in code wins.
|
|
491
|
+
|
|
492
|
+
Props: `title`, `greeting`, `launcher` (default true), `help` (the Help tab, default true; hidden while there is no published article), `sound` (default true), `preview` (default true), `askEmail` (default true), `position` ("bottom-right" default, "bottom-left", "bottom-center", "top-left", "top-center", "top-right", "middle-left", "middle-right"), `corners` ("round", "soft", "square"), `header` ("plain", "color"), `font` ("site", "system"), `label`, `theme`, `accent`. The panel is plain DOM, loaded with a dynamic import after the page rendered.
|
|
487
493
|
|
|
488
|
-
|
|
494
|
+
Files (0.15): a paperclip in the message field attaches pictures (PNG, JPEG, WebP, GIF) and PDFs, pasting an image (Cmd/Ctrl+V) or dropping files onto the panel does too, and where the browser has `getDisplayMedia` (not on phones) the paperclip offers "Screenshot of this page": the tab is shared once (`preferCurrentTab`), the messenger hides for one frame, the stream stops. Each file uploads when picked (`POST /api/sdk/messages/attachments`, multipart `key`, `distinctId`, `file`; the server checks the bytes, never SVG, at most 10 MB) and answers a signed id; `sendChat({ body, attachments: [id, …] })` sends up to 4, and the body may then be empty. Sent files come back as `message.attachments` and are read with `api.attachmentUrl(id)` (the visitor's own chat only). Each message also sends `context: { viewport }`; the server adds browser and system from the user agent. In React Native the paperclip shows when `expo-image-picker` is installed (optional peer) and picks from the photo library; the app sends its system and screen size.
|
|
489
495
|
|
|
490
496
|
Call `identify(user.id, { name, email })` and chats carry the person's name and email (they are not asked). While the panel is open it reads `GET /api/sdk/messages` every 4 seconds; while a visitor waits for an answer every 8 seconds for ten minutes, then every 30. Articles come from `GET /api/sdk/articles` as blocks, written with textContent, never HTML.
|
|
491
497
|
|
|
@@ -495,7 +501,7 @@ On sites with the script tag (Webflow, Framer, WordPress …) add `data-messenge
|
|
|
495
501
|
<script async src="https://app.mrrocket.dev/sdk.js" data-key="mrr_pub_…" data-messenger="true" data-messenger-title="Acme"></script>
|
|
496
502
|
```
|
|
497
503
|
|
|
498
|
-
Also `data-messenger-greeting`, `-label`, `-position="
|
|
504
|
+
Also `data-messenger-greeting`, `-label`, `-position="top-right"`, `-corners="soft"`, `-header="color"`, `-font="system"`, and "false" for `-help`, `-sound`, `-preview`, `-ask-email`. `mrrocket.messenger.open()` opens it from your code.
|
|
499
505
|
|
|
500
506
|
In React Native, `<MRRocketProvider messenger={{ launcher: true }}>` adds the bubble, `useMessenger().open()` opens it from your own button; a new answer shows as a banner at the top, with a light haptic when `expo-haptics` is installed. The provider also shows your live in-app survey (`surveys={false}` to switch off) and the feedback sheet (`useFeedback().open()`). With the person's consent (`replay`) and `react-native-view-shot` installed, the app also records sessions as screen pictures for Sessions: none while the keyboard is open, and `<MRRocketMask>` areas are painted black on the server before storing. Taps go to Heatmaps as clicks on the current screen; the provider then wraps the app in one `View` with `flex: 1` (`heatmaps={false}` to switch off).
|
|
501
507
|
|
package/dist/api-client.d.ts
CHANGED
|
@@ -76,6 +76,32 @@ export interface SurveyDefinition {
|
|
|
76
76
|
questions: SurveyQuestion[];
|
|
77
77
|
}
|
|
78
78
|
export type SurveyAnswer = number | string | string[];
|
|
79
|
+
/** A file in the messenger's chat: a screenshot, a photo or a PDF the visitor sent (servers from SDK 0.15 on). */
|
|
80
|
+
export interface ChatFile {
|
|
81
|
+
/** From an upload: the signed id a message sends. In a message: the file's own id (`attachmentUrl` reads it). */
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
type: "image/png" | "image/jpeg" | "image/webp" | "image/gif" | "application/pdf";
|
|
85
|
+
/** Bytes. */
|
|
86
|
+
size: number;
|
|
87
|
+
}
|
|
88
|
+
/** What the messenger can send: pictures and PDFs, at most 10 MB each, 4 per message. */
|
|
89
|
+
export declare const CHAT_FILE_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif", "application/pdf"];
|
|
90
|
+
export declare const CHAT_FILE_MAX_BYTES: number;
|
|
91
|
+
export declare const CHAT_FILES_PER_MESSAGE = 4;
|
|
92
|
+
/** A file to upload: a Blob or File on the web, the image picker's `{ uri, name, type }` in React Native. */
|
|
93
|
+
export type ChatUpload = Blob | {
|
|
94
|
+
uri: string;
|
|
95
|
+
name: string;
|
|
96
|
+
type: string;
|
|
97
|
+
};
|
|
98
|
+
/** The bug context a visitor message carries; the server fills browser and system from the user agent when left out. */
|
|
99
|
+
export interface ChatContext {
|
|
100
|
+
browser?: string;
|
|
101
|
+
os?: string;
|
|
102
|
+
/** "1440×900". */
|
|
103
|
+
viewport?: string;
|
|
104
|
+
}
|
|
79
105
|
/** One message of the messenger's chat. "visitor" is the person on the site; the rest come from the product's team. */
|
|
80
106
|
export interface ChatMessage {
|
|
81
107
|
id: string;
|
|
@@ -87,6 +113,8 @@ export interface ChatMessage {
|
|
|
87
113
|
id: string;
|
|
88
114
|
title: string;
|
|
89
115
|
}[];
|
|
116
|
+
/** Files sent with it (servers from SDK 0.15 on). */
|
|
117
|
+
attachments?: ChatFile[];
|
|
90
118
|
}
|
|
91
119
|
/** A published help article in a list (the messenger's Help tab). */
|
|
92
120
|
export interface ArticleSummary {
|
|
@@ -124,11 +152,25 @@ export interface Article {
|
|
|
124
152
|
updatedAt: string;
|
|
125
153
|
blocks: ArticleBlock[];
|
|
126
154
|
}
|
|
155
|
+
/** Where the chat button sits: a corner, the middle of the top or bottom edge, or the middle of a side. */
|
|
156
|
+
export type MessengerPosition = "top-left" | "top-center" | "top-right" | "middle-left" | "middle-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
157
|
+
/** How round the messenger is. */
|
|
158
|
+
export type MessengerCorners = "round" | "soft" | "square";
|
|
159
|
+
/** The radii per corner style, in px: the panel or sheet, message bubbles (and their tail), cards, fields and buttons. */
|
|
160
|
+
export declare const CORNERS: Record<MessengerCorners, {
|
|
161
|
+
panel: number;
|
|
162
|
+
bubble: number;
|
|
163
|
+
tail: number;
|
|
164
|
+
card: number;
|
|
165
|
+
field: number;
|
|
166
|
+
}>;
|
|
127
167
|
/** The messenger's look and texts as set in MRRocket › Support › Messenger (servers from SDK 0.9 on). */
|
|
128
168
|
export interface MessengerLook {
|
|
129
169
|
accent: string;
|
|
170
|
+
/** Up to two more colors blended into Home's background glow (MRRocket from 0.16; older servers leave it out). */
|
|
171
|
+
glow?: string[];
|
|
130
172
|
theme: "light" | "dark" | "auto";
|
|
131
|
-
position:
|
|
173
|
+
position: MessengerPosition;
|
|
132
174
|
title: string;
|
|
133
175
|
greeting: string;
|
|
134
176
|
label: string;
|
|
@@ -140,7 +182,19 @@ export interface MessengerLook {
|
|
|
140
182
|
sound: boolean;
|
|
141
183
|
preview: boolean;
|
|
142
184
|
askEmail: boolean;
|
|
185
|
+
/** How round panel, bubbles, cards and fields are (MRRocket from 0.15; older servers leave it out: round). */
|
|
186
|
+
corners?: MessengerCorners;
|
|
187
|
+
/** The site's own font (the default) or the system's (web only; from 0.15). */
|
|
188
|
+
font?: "site" | "system";
|
|
189
|
+
/** Home's top: plain like the rest, or in the accent color (from 0.15; older servers leave it out: plain). */
|
|
190
|
+
header?: "plain" | "color";
|
|
191
|
+
/** "Powered by MRRocket" at the bottom; the server sends false only on plans that may hide it (from 0.15). */
|
|
192
|
+
branding?: boolean;
|
|
193
|
+
/** The product's icon (its favicon) at the top left of Home; without it the title's first letter. */
|
|
194
|
+
logo?: string;
|
|
143
195
|
}
|
|
196
|
+
/** The link behind "Powered by MRRocket". */
|
|
197
|
+
export declare const BRAND_URL = "https://mrrocket.dev/?utm_source=messenger&utm_medium=powered-by";
|
|
144
198
|
/** The line under "Send us a message" and in the chat's head. */
|
|
145
199
|
export declare const replyLine: (t: MessengerLook["replyTime"]) => "We usually reply in a few minutes" | "We usually reply within a day" | "We usually reply in a few hours";
|
|
146
200
|
/** Black or white, whichever reads better on the accent (a light custom color gets dark text). */
|
|
@@ -203,11 +257,18 @@ export declare const createApi: (c: Conn) => {
|
|
|
203
257
|
seconds: number;
|
|
204
258
|
}) => Promise<unknown>;
|
|
205
259
|
chat: (signal?: AbortSignal) => Promise<ChatThread>;
|
|
260
|
+
/** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */
|
|
206
261
|
sendChat: (m: {
|
|
207
262
|
body: string;
|
|
208
263
|
email?: string;
|
|
209
264
|
url?: string;
|
|
265
|
+
attachments?: string[];
|
|
266
|
+
context?: ChatContext;
|
|
210
267
|
}) => Promise<ChatThread>;
|
|
268
|
+
/** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
|
|
269
|
+
uploadChatFile: (file: ChatUpload, signal?: AbortSignal) => Promise<ChatFile>;
|
|
270
|
+
/** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
|
|
271
|
+
attachmentUrl: (id: string) => string;
|
|
211
272
|
articles: (q?: string, signal?: AbortSignal) => Promise<{
|
|
212
273
|
articles: ArticleSummary[];
|
|
213
274
|
}>;
|
package/dist/api-client.js
CHANGED
|
@@ -5,6 +5,15 @@ var STATUS_LABEL = {
|
|
|
5
5
|
progress: "In progress",
|
|
6
6
|
shipped: "Shipped"
|
|
7
7
|
};
|
|
8
|
+
var CHAT_FILE_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "application/pdf"];
|
|
9
|
+
var CHAT_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
|
10
|
+
var CHAT_FILES_PER_MESSAGE = 4;
|
|
11
|
+
var CORNERS = {
|
|
12
|
+
round: { panel: 22, bubble: 18, tail: 6, card: 16, field: 20 },
|
|
13
|
+
soft: { panel: 14, bubble: 12, tail: 4, card: 10, field: 10 },
|
|
14
|
+
square: { panel: 6, bubble: 6, tail: 2, card: 4, field: 4 }
|
|
15
|
+
};
|
|
16
|
+
var BRAND_URL = "https://mrrocket.dev/?utm_source=messenger&utm_medium=powered-by";
|
|
8
17
|
var replyLine = (t) => t === "minutes" ? "We usually reply in a few minutes" : t === "day" ? "We usually reply within a day" : "We usually reply in a few hours";
|
|
9
18
|
var inkOn = (hex) => {
|
|
10
19
|
const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
@@ -29,7 +38,8 @@ var call = async (url, init, signal) => {
|
|
|
29
38
|
res = await fetch(url, {
|
|
30
39
|
...init,
|
|
31
40
|
signal,
|
|
32
|
-
|
|
41
|
+
// A form (a file upload) sets its own multipart type with the boundary.
|
|
42
|
+
headers: typeof init?.body === "string" ? { "content-type": "application/json" } : void 0
|
|
33
43
|
});
|
|
34
44
|
} catch (e) {
|
|
35
45
|
if (e.name === "AbortError") throw e;
|
|
@@ -62,7 +72,19 @@ var createApi = (c) => {
|
|
|
62
72
|
seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
|
|
63
73
|
respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
|
|
64
74
|
chat: (signal) => call(`${base}/messages?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, void 0, signal),
|
|
75
|
+
/** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */
|
|
65
76
|
sendChat: (m) => post("/messages", m),
|
|
77
|
+
/** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
|
|
78
|
+
uploadChatFile: (file, signal) => {
|
|
79
|
+
const form = new FormData();
|
|
80
|
+
form.append("key", c.apiKey);
|
|
81
|
+
form.append("distinctId", c.distinctId);
|
|
82
|
+
if ("uri" in file) form.append("file", file);
|
|
83
|
+
else form.append("file", file, file.name || "file");
|
|
84
|
+
return call(`${base}/messages/attachments`, { method: "POST", body: form }, signal);
|
|
85
|
+
},
|
|
86
|
+
/** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
|
|
87
|
+
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`,
|
|
66
88
|
articles: (q, signal) => call(`${base}/articles?${qs({ key: c.apiKey, q })}`, void 0, signal),
|
|
67
89
|
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, void 0, signal),
|
|
68
90
|
messengerLook: (signal) => call(`${base}/messenger?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
@@ -71,6 +93,11 @@ var createApi = (c) => {
|
|
|
71
93
|
};
|
|
72
94
|
export {
|
|
73
95
|
ApiError,
|
|
96
|
+
BRAND_URL,
|
|
97
|
+
CHAT_FILES_PER_MESSAGE,
|
|
98
|
+
CHAT_FILE_MAX_BYTES,
|
|
99
|
+
CHAT_FILE_TYPES,
|
|
100
|
+
CORNERS,
|
|
74
101
|
STATUS_LABEL,
|
|
75
102
|
createApi,
|
|
76
103
|
inkOn,
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-client.ts"],"sourcesContent":["/**\n * The SDK's fetch client and the shapes it reads, without React, so the script tag's widgets (src/script) use it too.\n * `./api` re-exports all of it with the `useApi` hook.\n */\nexport type PostStatus = \"review\" | \"planned\" | \"progress\" | \"shipped\";\n\nexport const STATUS_LABEL: Record<PostStatus, string> = {\n review: \"Under review\",\n planned: \"Planned\",\n progress: \"In progress\",\n shipped: \"Shipped\",\n};\n\nexport interface FeedbackBoardInfo {\n id: string;\n label: string;\n}\n\nexport interface FeedbackPost {\n id: string;\n title: string;\n body: string;\n board: string;\n status: PostStatus;\n votes: number;\n comments: number;\n voted: boolean;\n createdAt: string;\n /** \"Lena K.\", \"Anonymous\" or \"Team\". Missing from servers before SDK 0.6. */\n author?: string;\n}\n\n/** The product's public face for the board's header. */\nexport interface FeedbackProduct {\n name: string;\n logo: string | null;\n /** The domain's favicon, tried when `logo` does not load. Older servers leave it out. */\n logoFallback?: string | null;\n}\n\nexport interface FeedbackList {\n boards: FeedbackBoardInfo[];\n posts: FeedbackPost[];\n product?: FeedbackProduct;\n}\n\nexport interface FeedbackRoadmap {\n /** `review` (Backlog) comes from servers of SDK 0.6 on. */\n columns: { review?: FeedbackPost[]; planned: FeedbackPost[]; progress: FeedbackPost[]; shipped: FeedbackPost[] };\n}\n\nexport interface ChangelogEntry {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n}\n\nexport interface FeedbackComment {\n id: string;\n body: string;\n author: string;\n isTeam: boolean;\n postedAt: string;\n}\n\nexport type SurveyQuestionKind = \"nps\" | \"rating\" | \"single\" | \"multi\" | \"text\";\n\nexport interface SurveyQuestion {\n key: string;\n kind: SurveyQuestionKind;\n title: string;\n description?: string;\n required: boolean;\n options?: string[];\n scale?: number;\n low?: string;\n high?: string;\n placeholder?: string;\n}\n\nexport interface SurveyDefinition {\n id: string;\n name: string;\n type: string;\n thanks: string;\n questions: SurveyQuestion[];\n}\n\nexport type SurveyAnswer = number | string | string[];\n\n/** One message of the messenger's chat. \"visitor\" is the person on the site; the rest come from the product's team. */\nexport interface ChatMessage {\n id: string;\n from: \"visitor\" | \"team\" | \"ai\" | \"auto\";\n body: string;\n at: string;\n /** Help articles an answer drew on (servers from SDK 0.9 on). */\n articles?: { id: string; title: string }[];\n}\n\n/** A published help article in a list (the messenger's Help tab). */\nexport interface ArticleSummary {\n id: string;\n title: string;\n collection: string;\n excerpt: string;\n updatedAt: string;\n}\n\n/** A run of text inside an article block. Links are http(s), mailto or tel only. */\nexport interface ArticleSpan {\n text: string;\n href?: string;\n bold?: boolean;\n italic?: boolean;\n code?: boolean;\n}\n\n/** An article's content as blocks, written with textContent (never parsed as HTML). */\nexport type ArticleBlock =\n | { type: \"h2\" | \"h3\" | \"p\" | \"quote\"; spans: ArticleSpan[] }\n | { type: \"ul\" | \"ol\"; items: ArticleSpan[][] }\n | { type: \"code\"; text: string }\n | { type: \"hr\" };\n\nexport interface Article {\n id: string;\n title: string;\n collection: string;\n updatedAt: string;\n blocks: ArticleBlock[];\n}\n\n/** The messenger's look and texts as set in MRRocket › Support › Messenger (servers from SDK 0.9 on). */\nexport interface MessengerLook {\n accent: string;\n theme: \"light\" | \"dark\" | \"auto\";\n position: \"bottom-right\" | \"bottom-left\";\n title: string;\n greeting: string;\n label: string;\n /** How soon the team usually answers (MRRocket from 0.10; older servers leave it out). */\n replyTime?: \"minutes\" | \"hours\" | \"day\";\n /** A round chat button in the app (React Native) too. */\n appLauncher?: boolean;\n help: boolean;\n sound: boolean;\n preview: boolean;\n askEmail: boolean;\n}\n\n/** The line under \"Send us a message\" and in the chat's head. */\nexport const replyLine = (t: MessengerLook[\"replyTime\"]) =>\n t === \"minutes\" ? \"We usually reply in a few minutes\" : t === \"day\" ? \"We usually reply within a day\" : \"We usually reply in a few hours\";\n\n/** Black or white, whichever reads better on the accent (a light custom color gets dark text). */\nexport const inkOn = (hex: string) => {\n const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);\n if (!m) return \"#ffffff\";\n const [r, g, b] = [m[1], m[2], m[3]].map((x) => {\n const c = parseInt(x, 16) / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.45 ? \"#111214\" : \"#ffffff\";\n};\n\n/** The visitor's newest chat (open or closed) and its messages. */\nexport interface ChatThread {\n conversation: { id: string; status: \"open\" | \"solved\" } | null;\n messages: ChatMessage[];\n /** The visitor gave an email already: the messenger stops asking. */\n hasEmail: boolean;\n}\n\nexport class ApiError extends Error {\n status: number;\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}\n\ninterface Conn {\n host: string;\n apiKey: string;\n distinctId: string;\n user?: { id?: string; [key: string]: unknown };\n}\n\n// Not URLSearchParams: React Native's lacks `set` in older versions, and the apps use this client too.\nconst qs = (params: Record<string, string | undefined>) =>\n Object.entries(params)\n .filter((e): e is [string, string] => !!e[1])\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n\nconst call = async <T>(url: string, init: RequestInit | undefined, signal?: AbortSignal): Promise<T> => {\n let res: Response;\n try {\n res = await fetch(url, {\n ...init,\n signal,\n headers: init?.body ? { \"content-type\": \"application/json\" } : undefined,\n });\n } catch (e) {\n if ((e as Error).name === \"AbortError\") throw e;\n throw new ApiError(\"Could not reach the server. Check your connection and try again.\", 0);\n }\n if (!res.ok) {\n let detail = \"\";\n try {\n detail = ((await res.json()) as { error?: string }).error ?? \"\";\n } catch {\n // Not JSON; the status is enough.\n }\n throw new ApiError(detail || (res.status === 429 ? \"Too many requests. Try again in a minute.\" : \"Something went wrong. Please try again.\"), res.status);\n }\n return (await res.json()) as T;\n};\n\n/** The small fetch client behind the widgets. Every read takes an AbortSignal so unmounting cancels it. */\nexport const createApi = (c: Conn) => {\n const base = `${c.host}/api/sdk`;\n const who = { key: c.apiKey, distinctId: c.distinctId, user: c.user };\n const post = <T>(path: string, body: Record<string, unknown>, signal?: AbortSignal) =>\n call<T>(`${base}${path}`, { method: \"POST\", body: JSON.stringify({ ...who, ...body }) }, signal);\n return {\n list: (o: { board?: string; sort?: \"top\" | \"new\"; q?: string }, signal?: AbortSignal) =>\n call<FeedbackList>(`${base}/feedback?${qs({ key: c.apiKey, distinctId: c.distinctId, board: o.board, sort: o.sort, q: o.q })}`, undefined, signal),\n post: (id: string, signal?: AbortSignal) =>\n call<FeedbackPost>(`${base}/feedback/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n roadmap: (signal?: AbortSignal) => call<FeedbackRoadmap>(`${base}/feedback/roadmap?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n changelog: (signal?: AbortSignal) => call<{ entries: ChangelogEntry[] }>(`${base}/feedback/changelog?${qs({ key: c.apiKey })}`, undefined, signal),\n submit: (p: { title: string; body?: string; board: string }) => post<FeedbackPost>(\"/feedback\", p),\n vote: (id: string) => post<{ voted: boolean; votes: number }>(`/feedback/${encodeURIComponent(id)}/vote`, {}),\n comments: (id: string, signal?: AbortSignal) =>\n call<{ comments: FeedbackComment[] }>(`${base}/feedback/${encodeURIComponent(id)}/comments?${qs({ key: c.apiKey })}`, undefined, signal),\n comment: (id: string, body: string) => post<FeedbackComment>(`/feedback/${encodeURIComponent(id)}/comments`, { body }),\n survey: (url: string, signal?: AbortSignal) =>\n call<{ survey: SurveyDefinition | null }>(`${base}/surveys?${qs({ key: c.apiKey, distinctId: c.distinctId, url })}`, undefined, signal),\n seen: (id: string) => post<unknown>(`/surveys/${encodeURIComponent(id)}/seen`, { user: undefined }),\n respond: (id: string, r: { answers: Record<string, SurveyAnswer>; completed: boolean; seconds: number }) =>\n post<unknown>(`/surveys/${encodeURIComponent(id)}/respond`, r),\n chat: (signal?: AbortSignal) => call<ChatThread>(`${base}/messages?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n sendChat: (m: { body: string; email?: string; url?: string }) => post<ChatThread>(\"/messages\", m),\n articles: (q?: string, signal?: AbortSignal) => call<{ articles: ArticleSummary[] }>(`${base}/articles?${qs({ key: c.apiKey, q })}`, undefined, signal),\n article: (id: string, signal?: AbortSignal) =>\n call<Article>(`${base}/articles/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n messengerLook: (signal?: AbortSignal) => call<MessengerLook>(`${base}/messenger?${qs({ key: c.apiKey })}`, undefined, signal),\n rateArticle: (id: string, helpful: boolean) => post<{ counted: boolean }>(`/articles/${encodeURIComponent(id)}/rate`, { helpful }),\n };\n};\n\nexport type Api = ReturnType<typeof createApi>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"sourcesContent":["/**\n * The SDK's fetch client and the shapes it reads, without React, so the script tag's widgets (src/script) use it too.\n * `./api` re-exports all of it with the `useApi` hook.\n */\nexport type PostStatus = \"review\" | \"planned\" | \"progress\" | \"shipped\";\n\nexport const STATUS_LABEL: Record<PostStatus, string> = {\n review: \"Under review\",\n planned: \"Planned\",\n progress: \"In progress\",\n shipped: \"Shipped\",\n};\n\nexport interface FeedbackBoardInfo {\n id: string;\n label: string;\n}\n\nexport interface FeedbackPost {\n id: string;\n title: string;\n body: string;\n board: string;\n status: PostStatus;\n votes: number;\n comments: number;\n voted: boolean;\n createdAt: string;\n /** \"Lena K.\", \"Anonymous\" or \"Team\". Missing from servers before SDK 0.6. */\n author?: string;\n}\n\n/** The product's public face for the board's header. */\nexport interface FeedbackProduct {\n name: string;\n logo: string | null;\n /** The domain's favicon, tried when `logo` does not load. Older servers leave it out. */\n logoFallback?: string | null;\n}\n\nexport interface FeedbackList {\n boards: FeedbackBoardInfo[];\n posts: FeedbackPost[];\n product?: FeedbackProduct;\n}\n\nexport interface FeedbackRoadmap {\n /** `review` (Backlog) comes from servers of SDK 0.6 on. */\n columns: { review?: FeedbackPost[]; planned: FeedbackPost[]; progress: FeedbackPost[]; shipped: FeedbackPost[] };\n}\n\nexport interface ChangelogEntry {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n}\n\nexport interface FeedbackComment {\n id: string;\n body: string;\n author: string;\n isTeam: boolean;\n postedAt: string;\n}\n\nexport type SurveyQuestionKind = \"nps\" | \"rating\" | \"single\" | \"multi\" | \"text\";\n\nexport interface SurveyQuestion {\n key: string;\n kind: SurveyQuestionKind;\n title: string;\n description?: string;\n required: boolean;\n options?: string[];\n scale?: number;\n low?: string;\n high?: string;\n placeholder?: string;\n}\n\nexport interface SurveyDefinition {\n id: string;\n name: string;\n type: string;\n thanks: string;\n questions: SurveyQuestion[];\n}\n\nexport type SurveyAnswer = number | string | string[];\n\n/** A file in the messenger's chat: a screenshot, a photo or a PDF the visitor sent (servers from SDK 0.15 on). */\nexport interface ChatFile {\n /** From an upload: the signed id a message sends. In a message: the file's own id (`attachmentUrl` reads it). */\n id: string;\n name: string;\n type: \"image/png\" | \"image/jpeg\" | \"image/webp\" | \"image/gif\" | \"application/pdf\";\n /** Bytes. */\n size: number;\n}\n\n/** What the messenger can send: pictures and PDFs, at most 10 MB each, 4 per message. */\nexport const CHAT_FILE_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\", \"application/pdf\"] as const;\nexport const CHAT_FILE_MAX_BYTES = 10 * 1024 * 1024;\nexport const CHAT_FILES_PER_MESSAGE = 4;\n\n/** A file to upload: a Blob or File on the web, the image picker's `{ uri, name, type }` in React Native. */\nexport type ChatUpload = Blob | { uri: string; name: string; type: string };\n\n/** The bug context a visitor message carries; the server fills browser and system from the user agent when left out. */\nexport interface ChatContext {\n browser?: string;\n os?: string;\n /** \"1440×900\". */\n viewport?: string;\n}\n\n/** One message of the messenger's chat. \"visitor\" is the person on the site; the rest come from the product's team. */\nexport interface ChatMessage {\n id: string;\n from: \"visitor\" | \"team\" | \"ai\" | \"auto\";\n body: string;\n at: string;\n /** Help articles an answer drew on (servers from SDK 0.9 on). */\n articles?: { id: string; title: string }[];\n /** Files sent with it (servers from SDK 0.15 on). */\n attachments?: ChatFile[];\n}\n\n/** A published help article in a list (the messenger's Help tab). */\nexport interface ArticleSummary {\n id: string;\n title: string;\n collection: string;\n excerpt: string;\n updatedAt: string;\n}\n\n/** A run of text inside an article block. Links are http(s), mailto or tel only. */\nexport interface ArticleSpan {\n text: string;\n href?: string;\n bold?: boolean;\n italic?: boolean;\n code?: boolean;\n}\n\n/** An article's content as blocks, written with textContent (never parsed as HTML). */\nexport type ArticleBlock =\n | { type: \"h2\" | \"h3\" | \"p\" | \"quote\"; spans: ArticleSpan[] }\n | { type: \"ul\" | \"ol\"; items: ArticleSpan[][] }\n | { type: \"code\"; text: string }\n | { type: \"hr\" };\n\nexport interface Article {\n id: string;\n title: string;\n collection: string;\n updatedAt: string;\n blocks: ArticleBlock[];\n}\n\n/** Where the chat button sits: a corner, the middle of the top or bottom edge, or the middle of a side. */\nexport type MessengerPosition = \"top-left\" | \"top-center\" | \"top-right\" | \"middle-left\" | \"middle-right\" | \"bottom-left\" | \"bottom-center\" | \"bottom-right\";\n\n/** How round the messenger is. */\nexport type MessengerCorners = \"round\" | \"soft\" | \"square\";\n\n/** The radii per corner style, in px: the panel or sheet, message bubbles (and their tail), cards, fields and buttons. */\nexport const CORNERS: Record<MessengerCorners, { panel: number; bubble: number; tail: number; card: number; field: number }> = {\n round: { panel: 22, bubble: 18, tail: 6, card: 16, field: 20 },\n soft: { panel: 14, bubble: 12, tail: 4, card: 10, field: 10 },\n square: { panel: 6, bubble: 6, tail: 2, card: 4, field: 4 },\n};\n\n/** The messenger's look and texts as set in MRRocket › Support › Messenger (servers from SDK 0.9 on). */\nexport interface MessengerLook {\n accent: string;\n /** Up to two more colors blended into Home's background glow (MRRocket from 0.16; older servers leave it out). */\n glow?: string[];\n theme: \"light\" | \"dark\" | \"auto\";\n position: MessengerPosition;\n title: string;\n greeting: string;\n label: string;\n /** How soon the team usually answers (MRRocket from 0.10; older servers leave it out). */\n replyTime?: \"minutes\" | \"hours\" | \"day\";\n /** A round chat button in the app (React Native) too. */\n appLauncher?: boolean;\n help: boolean;\n sound: boolean;\n preview: boolean;\n askEmail: boolean;\n /** How round panel, bubbles, cards and fields are (MRRocket from 0.15; older servers leave it out: round). */\n corners?: MessengerCorners;\n /** The site's own font (the default) or the system's (web only; from 0.15). */\n font?: \"site\" | \"system\";\n /** Home's top: plain like the rest, or in the accent color (from 0.15; older servers leave it out: plain). */\n header?: \"plain\" | \"color\";\n /** \"Powered by MRRocket\" at the bottom; the server sends false only on plans that may hide it (from 0.15). */\n branding?: boolean;\n /** The product's icon (its favicon) at the top left of Home; without it the title's first letter. */\n logo?: string;\n}\n\n/** The link behind \"Powered by MRRocket\". */\nexport const BRAND_URL = \"https://mrrocket.dev/?utm_source=messenger&utm_medium=powered-by\";\n\n/** The line under \"Send us a message\" and in the chat's head. */\nexport const replyLine = (t: MessengerLook[\"replyTime\"]) =>\n t === \"minutes\" ? \"We usually reply in a few minutes\" : t === \"day\" ? \"We usually reply within a day\" : \"We usually reply in a few hours\";\n\n/** Black or white, whichever reads better on the accent (a light custom color gets dark text). */\nexport const inkOn = (hex: string) => {\n const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);\n if (!m) return \"#ffffff\";\n const [r, g, b] = [m[1], m[2], m[3]].map((x) => {\n const c = parseInt(x, 16) / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.45 ? \"#111214\" : \"#ffffff\";\n};\n\n/** The visitor's newest chat (open or closed) and its messages. */\nexport interface ChatThread {\n conversation: { id: string; status: \"open\" | \"solved\" } | null;\n messages: ChatMessage[];\n /** The visitor gave an email already: the messenger stops asking. */\n hasEmail: boolean;\n}\n\nexport class ApiError extends Error {\n status: number;\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}\n\ninterface Conn {\n host: string;\n apiKey: string;\n distinctId: string;\n user?: { id?: string; [key: string]: unknown };\n}\n\n// Not URLSearchParams: React Native's lacks `set` in older versions, and the apps use this client too.\nconst qs = (params: Record<string, string | undefined>) =>\n Object.entries(params)\n .filter((e): e is [string, string] => !!e[1])\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n\nconst call = async <T>(url: string, init: RequestInit | undefined, signal?: AbortSignal): Promise<T> => {\n let res: Response;\n try {\n res = await fetch(url, {\n ...init,\n signal,\n // A form (a file upload) sets its own multipart type with the boundary.\n headers: typeof init?.body === \"string\" ? { \"content-type\": \"application/json\" } : undefined,\n });\n } catch (e) {\n if ((e as Error).name === \"AbortError\") throw e;\n throw new ApiError(\"Could not reach the server. Check your connection and try again.\", 0);\n }\n if (!res.ok) {\n let detail = \"\";\n try {\n detail = ((await res.json()) as { error?: string }).error ?? \"\";\n } catch {\n // Not JSON; the status is enough.\n }\n throw new ApiError(detail || (res.status === 429 ? \"Too many requests. Try again in a minute.\" : \"Something went wrong. Please try again.\"), res.status);\n }\n return (await res.json()) as T;\n};\n\n/** The small fetch client behind the widgets. Every read takes an AbortSignal so unmounting cancels it. */\nexport const createApi = (c: Conn) => {\n const base = `${c.host}/api/sdk`;\n const who = { key: c.apiKey, distinctId: c.distinctId, user: c.user };\n const post = <T>(path: string, body: Record<string, unknown>, signal?: AbortSignal) =>\n call<T>(`${base}${path}`, { method: \"POST\", body: JSON.stringify({ ...who, ...body }) }, signal);\n return {\n list: (o: { board?: string; sort?: \"top\" | \"new\"; q?: string }, signal?: AbortSignal) =>\n call<FeedbackList>(`${base}/feedback?${qs({ key: c.apiKey, distinctId: c.distinctId, board: o.board, sort: o.sort, q: o.q })}`, undefined, signal),\n post: (id: string, signal?: AbortSignal) =>\n call<FeedbackPost>(`${base}/feedback/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n roadmap: (signal?: AbortSignal) => call<FeedbackRoadmap>(`${base}/feedback/roadmap?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n changelog: (signal?: AbortSignal) => call<{ entries: ChangelogEntry[] }>(`${base}/feedback/changelog?${qs({ key: c.apiKey })}`, undefined, signal),\n submit: (p: { title: string; body?: string; board: string }) => post<FeedbackPost>(\"/feedback\", p),\n vote: (id: string) => post<{ voted: boolean; votes: number }>(`/feedback/${encodeURIComponent(id)}/vote`, {}),\n comments: (id: string, signal?: AbortSignal) =>\n call<{ comments: FeedbackComment[] }>(`${base}/feedback/${encodeURIComponent(id)}/comments?${qs({ key: c.apiKey })}`, undefined, signal),\n comment: (id: string, body: string) => post<FeedbackComment>(`/feedback/${encodeURIComponent(id)}/comments`, { body }),\n survey: (url: string, signal?: AbortSignal) =>\n call<{ survey: SurveyDefinition | null }>(`${base}/surveys?${qs({ key: c.apiKey, distinctId: c.distinctId, url })}`, undefined, signal),\n seen: (id: string) => post<unknown>(`/surveys/${encodeURIComponent(id)}/seen`, { user: undefined }),\n respond: (id: string, r: { answers: Record<string, SurveyAnswer>; completed: boolean; seconds: number }) =>\n post<unknown>(`/surveys/${encodeURIComponent(id)}/respond`, r),\n chat: (signal?: AbortSignal) => call<ChatThread>(`${base}/messages?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n /** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */\n sendChat: (m: { body: string; email?: string; url?: string; attachments?: string[]; context?: ChatContext }) => post<ChatThread>(\"/messages\", m),\n /** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */\n uploadChatFile: (file: ChatUpload, signal?: AbortSignal) => {\n const form = new FormData();\n form.append(\"key\", c.apiKey);\n form.append(\"distinctId\", c.distinctId);\n if (\"uri\" in file) form.append(\"file\", file as unknown as Blob);\n else form.append(\"file\", file, (file as Blob & { name?: string }).name || \"file\");\n return call<ChatFile>(`${base}/messages/attachments`, { method: \"POST\", body: form }, signal);\n },\n /** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */\n attachmentUrl: (id: string) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`,\n articles: (q?: string, signal?: AbortSignal) => call<{ articles: ArticleSummary[] }>(`${base}/articles?${qs({ key: c.apiKey, q })}`, undefined, signal),\n article: (id: string, signal?: AbortSignal) =>\n call<Article>(`${base}/articles/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, undefined, signal),\n messengerLook: (signal?: AbortSignal) => call<MessengerLook>(`${base}/messenger?${qs({ key: c.apiKey })}`, undefined, signal),\n rateArticle: (id: string, helpful: boolean) => post<{ counted: boolean }>(`/articles/${encodeURIComponent(id)}/rate`, { helpful }),\n };\n};\n\nexport type Api = ReturnType<typeof createApi>;\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(){'use strict';var
|
|
1
|
+
(function(){'use strict';var L=(n,e)=>Math.round(n*(1-e/100)*100)/100,T=(n,e)=>{try{return new Intl.NumberFormat(void 0,{style:"currency",currency:e}).format(n)}catch(c){return `${n.toFixed(2)} ${e}`}},A=n=>n.kind==="contact"?"Send to the team":n.kind==="pause"?"Pause instead":n.kind==="downgrade"?"Switch plan":"Accept offer";var U="ckf-styles",D=`
|
|
2
2
|
.ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}
|
|
3
3
|
.ckf-root[data-theme=dark]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}
|
|
4
4
|
@media (prefers-color-scheme:dark){.ckf-root[data-theme=auto]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}}
|
|
@@ -70,4 +70,4 @@
|
|
|
70
70
|
.ckf-banner{animation-name:ckf-fade}
|
|
71
71
|
@media (max-width:520px){.ckf-scrim{align-items:flex-end;padding:0}.ckf-scrim .ckf-card{max-width:none;border-radius:var(--ckf-radius) var(--ckf-radius) 0 0;max-height:92dvh}.ckf-float{right:12px;bottom:12px}}
|
|
72
72
|
@media (prefers-reduced-motion:reduce){.ckf-root *{animation:none!important;transition:none!important}}
|
|
73
|
-
`,t=(n,e={},...a)=>{let s=document.createElement(n);for(let[g,p]of Object.entries(e))p===void 0||p===false||(typeof p=="function"?s.addEventListener(g.slice(2).toLowerCase(),p):g==="className"?s.className=String(p):s.setAttribute(g,p===true?"":p));for(let g of a)(g||g==="")&&s.append(typeof g=="string"?document.createTextNode(g):g);return s},P=(n,e,a,s={})=>t("button",{type:"button",className:`ckf-btn${e==="secondary"?"":` ckf-${e}`}`,disabled:!!s.disabled,"data-ckf-id":s.id,onClick:()=>a()},n),R=(n,e)=>t("button",{type:"button",className:"ckf-x","aria-label":n,"data-ckf-id":"x",onClick:()=>e()},"\xD7"),A=(n,e)=>e>1?t("span",{className:"ckf-dots",role:"img","aria-label":`Step ${n} of ${e}`},...Array.from({length:e},(a,s)=>t("span",{className:"ckf-dot","data-on":s<n}))):t("span"),z=n=>n.type==="cancel"?`c:${n.screen}`:n.type==="step"?`s:${n.step.id}`:n.type==="checklist"?`l:${n.flow.id}`:`m:${n.message.id}`,V=n=>{var a;if(!document.getElementById(L)){let s=document.createElement("style");s.id=L,s.textContent=q,document.head.appendChild(s);}let e=t("div",{className:`ckf-root${n.className?` ${n.className}`:""}`,"data-theme":(a=n.theme)!=null?a:"auto"});return n.accent&&e.style.setProperty("--ckf-accent",n.accent),document.body.appendChild(e),e},H=(n,e={})=>{if(typeof document=="undefined")return ()=>{};let a=V(e),s="",g=null,p="",h=null,y=r=>{var m,x;let o="ckf-title",c=r.busy,f=[];if(r.screen==="survey")f.push(t("h2",{className:"ckf-h",id:o},r.flow.question||"Why are you leaving?"),t("div",{className:"ckf-opts",role:"radiogroup","aria-labelledby":o},...r.flow.reasons.map(l=>t("button",{type:"button",role:"radio",className:"ckf-opt","aria-checked":String(r.reason===l.id),"data-ckf-id":`r:${l.id}`,onClick:()=>n.cancel.pick(l.id)},t("span",{className:"ckf-radio"}),l.label))),t("div",{className:"ckf-btns"},P("Continue","primary",n.cancel.next,{disabled:!r.reason,id:"go"}),P("Never mind, keep my plan","quiet",n.cancel.close)));else if(r.screen==="offer"&&r.offer){let l=r.offer,C=(m=r.flow.reasons.find(S=>S.id===r.reason))==null?void 0:m.label,N=false;l.kind==="discount"&&l.percent&&(N=r.price?t("div",{className:"ckf-box"},t("span",{className:"ckf-big"},M(_(r.price.amount,l.percent),r.price.currency)),t("span",{className:"ckf-strike"},M(r.price.amount,r.price.currency)),t("span",{className:"ckf-small"},`/${(x=r.price.interval)!=null?x:"month"}${l.months?` for ${l.months} ${l.months===1?"month":"months"}`:""}`)):t("div",{className:"ckf-box"},t("span",{className:"ckf-big"},`${l.percent}% off`),l.months?t("span",{className:"ckf-small"},`for ${l.months} ${l.months===1?"month":"months"}`):false)),l.kind==="pause"&&l.months&&(N=t("div",{className:"ckf-box"},t("span",{},`Pause for up to ${l.months} ${l.months===1?"month":"months"}. Your data stays.`))),l.kind==="downgrade"&&l.planName&&(N=t("div",{className:"ckf-box"},t("b",{},l.planName)));let E=l.kind==="contact"?t("textarea",{className:"ckf-text",placeholder:"What would make you stay?","aria-label":"Your message",maxlength:"500","data-ckf-id":"comment",onInput:S=>p=S.target.value}):null;E&&(E.value=p),f.push(C?t("p",{className:"ckf-small"},`You said: ${C}`):false,t("h2",{className:"ckf-h",id:o},l.headline),N,E!=null?E:false,t("div",{className:"ckf-btns"},P(c?"One moment\u2026":T(l),"primary",()=>n.cancel.accept(p),{disabled:c,id:"go"}),P("No thanks, continue cancelling","quiet",n.cancel.decline,{disabled:c})));}else {let C=r.steps===1?null:t("textarea",{className:"ckf-text",placeholder:"Anything else we should know? (optional)","aria-label":"Anything else we should know? (optional)",maxlength:"500","data-ckf-id":"comment",onInput:N=>p=N.target.value});C&&(C.value=p),f.push(t("h2",{className:"ckf-h",id:o},r.flow.confirm.title),r.flow.confirm.body?t("p",{className:"ckf-p"},r.flow.confirm.body):false,C!=null?C:false,t("div",{className:"ckf-btns"},P(c?"One moment\u2026":r.flow.confirm.button,"danger",()=>n.cancel.confirm(p),{disabled:c,id:"go"}),P("Keep my plan","secondary",n.cancel.close,{disabled:c})));}return t("div",{className:"ckf-scrim",onClick:l=>l.target===l.currentTarget&&!c&&n.cancel.close()},t("div",{className:"ckf-card",role:"dialog","aria-modal":"true","aria-labelledby":o},t("div",{className:"ckf-top"},A(r.step,r.steps),R("Close",n.cancel.close)),...f,r.error?t("p",{className:"ckf-err",role:"alert"},r.error):false))},u=r=>{let o=r.step,c=t("div",{className:"ckf-card",role:"dialog","aria-modal":o.kind==="modal"?"true":void 0,"aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},r.total>1?t("p",{className:"ckf-small"},`Step ${r.index+1} of ${r.total}`):t("span"),R("Close the tour",n.onboarding.dismiss)),t("h2",{className:"ckf-h",id:"ckf-title"},o.title),o.body?t("p",{className:"ckf-p"},o.body):false,t("div",{className:"ckf-row"},P(o.button||(r.last?"Done":"Next"),"primary",n.onboarding.next,{id:"go"})));if(o.kind==="modal")return t("div",{className:"ckf-scrim"},c);let f=o.target?i(o.target):null,m=t("div",{className:"ckf-float"},c);if(f){let x=t("div",{className:"ckf-ring","aria-hidden":"true"});return h={float:m,ring:x,target:f},b(m,x,f),t("div",{},x,m)}return m},i=r=>{var c;let o=()=>document.querySelector(`[data-ck-target="${CSS.escape(r)}"]`);return /^[#.[]|[\s>:=]/.test(r)&&(c=d(r))!=null?c:o()},d=r=>{try{return document.querySelector(r)}catch(o){return null}},b=(r,o,c)=>{let f=c.getBoundingClientRect();Object.assign(o.style,{left:`${f.left-4}px`,top:`${f.top-4}px`,width:`${f.width+8}px`,height:`${f.height+8}px`});let m=Math.min(340,window.innerWidth-24),x=Math.max(12,Math.min(f.left,window.innerWidth-m-12)),l=f.bottom+12;Object.assign(r.style,{left:`${x}px`,right:"auto",width:`${m}px`}),l+200<window.innerHeight?Object.assign(r.style,{top:`${l}px`,bottom:"auto"}):Object.assign(r.style,{top:"auto",bottom:`${Math.max(12,window.innerHeight-f.top+12)}px`});},w=r=>{let o=r.items.filter(c=>c.done).length;return t("div",{className:"ckf-float"},t("div",{className:"ckf-card",role:"region","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},t("h2",{className:"ckf-h",id:"ckf-title"},r.flow.name),R("Hide the checklist",n.onboarding.dismiss)),t("p",{className:"ckf-small"},`${o} of ${r.items.length} done`),t("div",{className:"ckf-bar",role:"progressbar","aria-valuemin":"0","aria-valuemax":String(r.items.length),"aria-valuenow":String(o)},t("i",{style:`width:${o/r.items.length*100}%`})),t("div",{className:"ckf-opts"},...r.items.map(({step:c,done:f})=>{let m=[t("span",{className:"ckf-tick","aria-hidden":"true"},f?"\u2713":""),t("span",{},t("b",{},c.title),c.body?t("span",{className:"ckf-small"},c.body):false)];return f||c.event?t("div",{className:"ckf-check","data-done":f,role:"listitem"},...m):t("button",{type:"button",className:"ckf-check","aria-label":`Mark \u201C${c.title}\u201D done`,"data-ckf-id":`i:${c.id}`,onClick:()=>n.onboarding.check(c.id)},...m)}))))},v=r=>{let o=r.message,c=o.button?P(o.button,"primary",n.message.click,{id:"go"}):false;return o.style==="banner"?t("div",{className:"ckf-banner",role:"status"},t("span",{},t("b",{},o.title),o.body?t("span",{className:"ckf-small"},o.body):false),c,R("Dismiss",n.message.dismiss)):t("div",{className:"ckf-scrim",onClick:f=>f.target===f.currentTarget&&n.message.dismiss()},t("div",{className:"ckf-card",role:"dialog","aria-modal":"true","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},t("span"),R("Dismiss",n.message.dismiss)),t("h2",{className:"ckf-h",id:"ckf-title"},o.title),o.body?t("p",{className:"ckf-p"},o.body):false,c?t("div",{className:"ckf-row"},c):false))},F=()=>{var f,m,x,l,C;let r=n.view(),o=r?z(r):"",c=(m=(f=document.activeElement)==null?void 0:f.closest)!=null&&m.call(f,".ckf-root")?(x=document.activeElement)==null?void 0:x.getAttribute("data-ckf-id"):null;if(o&&!s&&document.activeElement instanceof HTMLElement&&!document.activeElement.closest(".ckf-root")&&(g=document.activeElement),o!==s&&!o.startsWith("c:")&&(p=""),o||(p=""),h=null,a.replaceChildren(...r?[r.type==="cancel"?y(r):r.type==="step"?u(r):r.type==="checklist"?w(r):v(r)]:[]),o!==s){let N=a.querySelector("[aria-modal=true]"),E=(l=N==null?void 0:N.querySelector("[role=radio], [data-ckf-id=go], textarea"))!=null?l:null;E==null||E.focus(),!o&&g&&(g.focus(),g=null);}else c&&((C=a.querySelector(`[data-ckf-id="${CSS.escape(c)}"]`))==null||C.focus());s=o;},k=r=>{if(r.key!=="Escape")return;let o=n.view();(o==null?void 0:o.type)==="cancel"?n.cancel.close():(o==null?void 0:o.type)==="message"?n.message.dismiss():(o==null?void 0:o.type)==="step"&&o.step.kind==="modal"&&n.onboarding.dismiss();},O=()=>{h&&b(h.float,h.ring,h.target);};document.addEventListener("keydown",k),window.addEventListener("resize",O),window.addEventListener("scroll",O,{passive:true,capture:true});let $=n.subscribe(F);return F(),()=>{$(),document.removeEventListener("keydown",k),window.removeEventListener("resize",O),window.removeEventListener("scroll",O,{capture:true}),a.remove();}},j=(n,e={})=>{if(typeof document=="undefined")return ()=>{};let a=V(e),s="",g=null,p=()=>{var w,v,F;let u=n.view(),i=u?`${u.paywall.id}:${u.index}`:"";if(i&&!s&&document.activeElement instanceof HTMLElement&&(g=document.activeElement),!u){a.replaceChildren(),a.removeAttribute("data-still"),s&&(g==null||g.focus()),s="";return}s&&a.setAttribute("data-still","");let d=u.page,b=d.kind==="plans"?t("div",{className:"ckf-opts",role:"radiogroup","aria-label":"Plans"},...u.plans.map(k=>t("button",{type:"button",role:"radio",className:"ckf-opt ckf-plan","aria-checked":String(u.plan===k.id),"data-ckf-id":`p:${k.id}`,onClick:()=>n.pick(k.id)},t("span",{className:"ckf-radio"}),t("span",{},k.name,k.note?t("span",{className:"ckf-small"},k.note):false),t("b",{},k.price)))):false;a.replaceChildren(t("div",{className:"ckf-scrim",onClick:k=>k.target===k.currentTarget&&n.close()},t("div",{className:"ckf-card ckf-pw",role:"dialog","aria-modal":"true","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},A(u.index+1,u.total),R("Close",n.close)),t("h2",{className:"ckf-h",id:"ckf-title"},d.title),d.body?t("p",{className:"ckf-p"},d.body):false,d.kind==="benefits"&&((w=d.items)!=null&&w.length)?t("div",{className:"ckf-opts"},...d.items.map(k=>t("p",{className:"ckf-li"},k))):false,d.kind==="proof"&&((v=d.quotes)!=null&&v.length)?t("div",{className:"ckf-opts"},...d.quotes.map(k=>t("blockquote",{className:"ckf-quote"},k.text,k.by?t("cite",{},k.by):false))):false,b,u.error?t("p",{className:"ckf-err",role:"alert"},u.error):false,t("div",{className:"ckf-btns"},P(u.busy?"One moment\u2026":d.button||"Continue","primary",n.next,{disabled:u.busy||d.kind==="plans"&&!u.plan,id:"go"}),u.index?P("Back","quiet",n.back,{disabled:u.busy}):false)))),i!==s&&((F=a.querySelector("[data-ckf-id=go]"))==null||F.focus()),s=i;},h=u=>{n.view()&&u.key==="Escape"&&n.close();};document.addEventListener("keydown",h);let y=n.subscribe(p);return p(),()=>{y(),document.removeEventListener("keydown",h),a.remove();}};var D=n=>{let e=2166136261;for(let a=0;a<n.length;a++)e^=n.charCodeAt(a),e=Math.imul(e,16777619);return (e>>>0)/4294967296};var U=(n,e)=>{var p,h,y,u;let a=(h=(p=n.variants[0])==null?void 0:p.id)!=null?h:"control";if(n.winner)return n.winner;if(n.status!=="running")return a;let s=D(`${n.key}:${e}`),g=0;for(let i of n.variants)if(g+=i.weight,s<g)return i.id;return (u=(y=n.variants[n.variants.length-1])==null?void 0:y.id)!=null?u:a};var I="$paywall",W=(n,e,a)=>{var h,y;let s=e.experiment?n==null?void 0:n.experiments.find(u=>u.key===e.experiment):void 0,g=s?U(s,a):(h=e.variants[0])==null?void 0:h.id,p=(y=e.variants.find(u=>u.id===g))!=null?y:e.variants[0];return p?{variant:p.id,pages:p.pages,test:(s==null?void 0:s.status)==="running"&&!s.winner?s.key:void 0}:null},K=n=>{let e=null,a=new Set,s=()=>{for(let i of [...a])try{i();}catch(d){}},g=()=>e?{paywall:e.w.id,placement:e.w.placement,variant:e.variant}:{},p=()=>{e&&!e.seen.has(e.index)&&(e.seen.add(e.index),n.track(I,{...g(),page:e.pages[e.index].id,index:e.index,action:"page"})),s();},h=i=>{var b,w;let d=e;d&&(e=null,i.outcome==="closed"&&((w=(b=d.options).onClose)==null||w.call(b,i)),d.resolve(i),s());},y=i=>{var d;return {outcome:i,index:(d=e==null?void 0:e.index)!=null?d:0,...e?{paywall:e.w.id,variant:e.variant}:{},...i!=="closed"&&e?{plan:e.plans.find(b=>b.id===e.plan)}:{}}},u=()=>{let i=e,d=i==null?void 0:i.plans.find(b=>b.id===i.plan);if(!(!i||!d||i.busy)){if(!i.options.onPurchase)return h(y("chosen"));i.busy=true,i.error=void 0,s(),Promise.resolve().then(()=>{var b,w;return (w=(b=i.options).onPurchase)==null?void 0:w.call(b,d)}).then(b=>{if(i.busy=false,e===i){if(b===false)return s();n.track(I,{...g(),page:i.pages[i.index].id,index:i.index,plan:d.id,action:"purchased"}),h(y("purchased"));}},()=>{i.busy=false,i.error="The purchase did not go through. Please try again.",s();});}};return {open:(i,d={})=>{var o,c,f;if(e)return e.promise;let b=n.config(),w=(o=b==null?void 0:b.paywalls)==null?void 0:o.find(m=>m.placement===i),v=w?W(b,w,n.distinctId()):null;if(!w||!(v!=null&&v.pages.length))return Promise.resolve({outcome:"closed",index:0});let F=new Map(((c=d.plans)!=null?c:[]).map(m=>[m.id,m])),k=w.plans.map(m=>{var x;return (x=F.get(m.id))!=null?x:m}),O=()=>{},$=new Promise(m=>O=m),r=k.some(m=>m.id===w.highlight)?w.highlight:(f=k[0])==null?void 0:f.id;return e={w,variant:v.variant,pages:v.pages,plans:k,index:0,plan:r,busy:false,seen:new Set,options:d,resolve:O,promise:$},v.test&&n.track("$exposure",{experiment:v.test,variant:v.variant}),p(),$},view:()=>e?{paywall:e.w,variant:e.variant,page:e.pages[e.index],index:e.index,total:e.pages.length,last:e.index===e.pages.length-1,plans:e.plans,plan:e.plan,busy:e.busy,error:e.error}:null,subscribe:i=>(a.add(i),()=>a.delete(i)),next:()=>{if(!(!e||e.busy)){if(e.pages[e.index].kind==="plans"||e.index===e.pages.length-1)return u();e.index++,e.error=void 0,p();}},back:()=>{!e||e.busy||!e.index||(e.index--,e.error=void 0,s());},pick:i=>{!e||e.busy||!e.plans.some(d=>d.id===i)||(e.plan=i,e.error=void 0,s());},close:()=>{!e||e.busy||(n.track(I,{...g(),index:e.index,action:"closed"}),h(y("closed")));}}};var B=(n,e)=>{let a=K(n);return j(a,e),a};window.__mrrocketFlows={mountFlows:H,paywallHost:B};})();
|
|
73
|
+
`,t=(n,e={},...c)=>{let s=document.createElement(n);for(let[g,u]of Object.entries(e))u===void 0||u===false||(typeof u=="function"?s.addEventListener(g.slice(2).toLowerCase(),u):g==="className"?s.className=String(u):s.setAttribute(g,u===true?"":u));for(let g of c)(g||g==="")&&s.append(typeof g=="string"?document.createTextNode(g):g);return s},P=(n,e,c,s={})=>t("button",{type:"button",className:`ckf-btn${e==="secondary"?"":` ckf-${e}`}`,disabled:!!s.disabled,"data-ckf-id":s.id,onClick:()=>c()},n),$=(n,e)=>t("button",{type:"button",className:"ckf-x","aria-label":n,"data-ckf-id":"x",onClick:()=>e()},"\xD7"),V=(n,e)=>e>1?t("span",{className:"ckf-dots",role:"img","aria-label":`Step ${n} of ${e}`},...Array.from({length:e},(c,s)=>t("span",{className:"ckf-dot","data-on":s<n}))):t("span"),W=n=>{var e;return n.type==="cancel"?`c:${n.screen}:${(e=n.round)!=null?e:1}`:n.type==="step"?`s:${n.step.id}`:n.type==="checklist"?`l:${n.flow.id}`:`m:${n.message.id}`},H=n=>{var c;if(!document.getElementById(U)){let s=document.createElement("style");s.id=U,s.textContent=D,document.head.appendChild(s);}let e=t("div",{className:`ckf-root${n.className?` ${n.className}`:""}`,"data-theme":(c=n.theme)!=null?c:"auto"});return n.accent&&e.style.setProperty("--ckf-accent",n.accent),document.body.appendChild(e),e},j=(n,e={})=>{if(typeof document=="undefined")return ()=>{};let c=H(e),s="",g=null,u="",w=null,C=r=>{var m,y,E,M;let o="ckf-title",l=r.busy,d=[];if(r.screen==="survey")d.push(t("h2",{className:"ckf-h",id:o},r.flow.question||"Why are you leaving?"),t("div",{className:"ckf-opts",role:"radiogroup","aria-labelledby":o},...r.flow.reasons.map(a=>t("button",{type:"button",role:"radio",className:"ckf-opt","aria-checked":String(r.reason===a.id),"data-ckf-id":`r:${a.id}`,onClick:()=>n.cancel.pick(a.id)},t("span",{className:"ckf-radio"}),a.label))),t("div",{className:"ckf-btns"},P("Continue","primary",n.cancel.next,{disabled:!r.reason,id:"go"}),P("Never mind, keep my plan","quiet",n.cancel.close)));else if(r.screen==="followup"&&r.followUp){let a=r.followUp,x;if(a.answers.length)x=t("div",{className:"ckf-opts",role:"radiogroup","aria-labelledby":o},...a.answers.map((v,S)=>t("button",{type:"button",role:"radio",className:"ckf-opt","aria-checked":String(r.answer===v),"data-ckf-id":`a:${S}`,onClick:()=>n.cancel.answer(v)},t("span",{className:"ckf-radio"}),v)));else {let v=t("textarea",{className:"ckf-text",placeholder:"Your answer (optional)","aria-labelledby":o,maxlength:"500","data-ckf-id":"answer",onInput:S=>n.cancel.answer(S.target.value)});v.value=(m=r.answer)!=null?m:"",x=v;}let O=(y=r.flow.reasons.find(v=>v.id===r.reason))==null?void 0:y.label;d.push(O?t("p",{className:"ckf-small"},`You said: ${O}`):false,t("h2",{className:"ckf-h",id:o},a.question),x,t("div",{className:"ckf-btns"},P("Continue","primary",n.cancel.next,{id:"go"}),P("Never mind, keep my plan","quiet",n.cancel.close)));}else if(r.screen==="offer"&&r.offer){let a=r.offer,x=(E=r.flow.reasons.find(S=>S.id===r.reason))==null?void 0:E.label,O=false;a.kind==="discount"&&a.percent&&(O=r.price?t("div",{className:"ckf-box"},t("span",{className:"ckf-big"},T(L(r.price.amount,a.percent),r.price.currency)),t("span",{className:"ckf-strike"},T(r.price.amount,r.price.currency)),t("span",{className:"ckf-small"},`/${(M=r.price.interval)!=null?M:"month"}${a.months?` for ${a.months} ${a.months===1?"month":"months"}`:""}`)):t("div",{className:"ckf-box"},t("span",{className:"ckf-big"},`${a.percent}% off`),a.months?t("span",{className:"ckf-small"},`for ${a.months} ${a.months===1?"month":"months"}`):false)),a.kind==="pause"&&a.months&&(O=t("div",{className:"ckf-box"},t("span",{},`Pause for up to ${a.months} ${a.months===1?"month":"months"}. Your data stays.`))),a.kind==="downgrade"&&a.planName&&(O=t("div",{className:"ckf-box"},t("b",{},a.planName)));let v=a.kind==="contact"?t("textarea",{className:"ckf-text",placeholder:"What would make you stay?","aria-label":"Your message",maxlength:"500","data-ckf-id":"comment",onInput:S=>u=S.target.value}):null;v&&(v.value=u),d.push(x?t("p",{className:"ckf-small"},`You said: ${x}`):false,t("h2",{className:"ckf-h",id:o},a.headline),O,v!=null?v:false,t("div",{className:"ckf-btns"},P(l?"One moment\u2026":A(a),"primary",()=>n.cancel.accept(u),{disabled:l,id:"go"}),P("No thanks, continue cancelling","quiet",n.cancel.decline,{disabled:l})));}else {let x=r.steps===1?null:t("textarea",{className:"ckf-text",placeholder:"Anything else we should know? (optional)","aria-label":"Anything else we should know? (optional)",maxlength:"500","data-ckf-id":"comment",onInput:O=>u=O.target.value});x&&(x.value=u),d.push(t("h2",{className:"ckf-h",id:o},r.flow.confirm.title),r.flow.confirm.body?t("p",{className:"ckf-p"},r.flow.confirm.body):false,x!=null?x:false,t("div",{className:"ckf-btns"},P(l?"One moment\u2026":r.flow.confirm.button,"danger",()=>n.cancel.confirm(u),{disabled:l,id:"go"}),P("Keep my plan","secondary",n.cancel.close,{disabled:l})));}return t("div",{className:"ckf-scrim",onClick:a=>a.target===a.currentTarget&&!l&&n.cancel.close()},t("div",{className:"ckf-card",role:"dialog","aria-modal":"true","aria-labelledby":o},t("div",{className:"ckf-top"},V(r.step,r.steps),$("Close",n.cancel.close)),...d,r.error?t("p",{className:"ckf-err",role:"alert"},r.error):false))},p=r=>{let o=r.step,l=t("div",{className:"ckf-card",role:"dialog","aria-modal":o.kind==="modal"?"true":void 0,"aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},r.total>1?t("p",{className:"ckf-small"},`Step ${r.index+1} of ${r.total}`):t("span"),$("Close the tour",n.onboarding.dismiss)),t("h2",{className:"ckf-h",id:"ckf-title"},o.title),o.body?t("p",{className:"ckf-p"},o.body):false,t("div",{className:"ckf-row"},P(o.button||(r.last?"Done":"Next"),"primary",n.onboarding.next,{id:"go"})));if(o.kind==="modal")return t("div",{className:"ckf-scrim"},l);let d=o.target?i(o.target):null,m=t("div",{className:"ckf-float"},l);if(d){let y=t("div",{className:"ckf-ring","aria-hidden":"true"});return w={float:m,ring:y,target:d},b(m,y,d),t("div",{},y,m)}return m},i=r=>{var l;let o=()=>document.querySelector(`[data-ck-target="${CSS.escape(r)}"]`);return /^[#.[]|[\s>:=]/.test(r)&&(l=f(r))!=null?l:o()},f=r=>{try{return document.querySelector(r)}catch(o){return null}},b=(r,o,l)=>{let d=l.getBoundingClientRect();Object.assign(o.style,{left:`${d.left-4}px`,top:`${d.top-4}px`,width:`${d.width+8}px`,height:`${d.height+8}px`});let m=Math.min(340,window.innerWidth-24),y=Math.max(12,Math.min(d.left,window.innerWidth-m-12)),E=d.bottom+12;Object.assign(r.style,{left:`${y}px`,right:"auto",width:`${m}px`}),E+200<window.innerHeight?Object.assign(r.style,{top:`${E}px`,bottom:"auto"}):Object.assign(r.style,{top:"auto",bottom:`${Math.max(12,window.innerHeight-d.top+12)}px`});},h=r=>{let o=r.items.filter(l=>l.done).length;return t("div",{className:"ckf-float"},t("div",{className:"ckf-card",role:"region","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},t("h2",{className:"ckf-h",id:"ckf-title"},r.flow.name),$("Hide the checklist",n.onboarding.dismiss)),t("p",{className:"ckf-small"},`${o} of ${r.items.length} done`),t("div",{className:"ckf-bar",role:"progressbar","aria-valuemin":"0","aria-valuemax":String(r.items.length),"aria-valuenow":String(o)},t("i",{style:`width:${o/r.items.length*100}%`})),t("div",{className:"ckf-opts"},...r.items.map(({step:l,done:d})=>{let m=[t("span",{className:"ckf-tick","aria-hidden":"true"},d?"\u2713":""),t("span",{},t("b",{},l.title),l.body?t("span",{className:"ckf-small"},l.body):false)];return d||l.event?t("div",{className:"ckf-check","data-done":d,role:"listitem"},...m):t("button",{type:"button",className:"ckf-check","aria-label":`Mark \u201C${l.title}\u201D done`,"data-ckf-id":`i:${l.id}`,onClick:()=>n.onboarding.check(l.id)},...m)}))))},N=r=>{let o=r.message,l=o.button?P(o.button,"primary",n.message.click,{id:"go"}):false;return o.style==="banner"?t("div",{className:"ckf-banner",role:"status"},t("span",{},t("b",{},o.title),o.body?t("span",{className:"ckf-small"},o.body):false),l,$("Dismiss",n.message.dismiss)):t("div",{className:"ckf-scrim",onClick:d=>d.target===d.currentTarget&&n.message.dismiss()},t("div",{className:"ckf-card",role:"dialog","aria-modal":"true","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},t("span"),$("Dismiss",n.message.dismiss)),t("h2",{className:"ckf-h",id:"ckf-title"},o.title),o.body?t("p",{className:"ckf-p"},o.body):false,l?t("div",{className:"ckf-row"},l):false))},F=()=>{var d,m,y,E,M;let r=n.view(),o=r?W(r):"",l=(m=(d=document.activeElement)==null?void 0:d.closest)!=null&&m.call(d,".ckf-root")?(y=document.activeElement)==null?void 0:y.getAttribute("data-ckf-id"):null;if(o&&!s&&document.activeElement instanceof HTMLElement&&!document.activeElement.closest(".ckf-root")&&(g=document.activeElement),o!==s&&!o.startsWith("c:")&&(u=""),o||(u=""),w=null,c.replaceChildren(...r?[r.type==="cancel"?C(r):r.type==="step"?p(r):r.type==="checklist"?h(r):N(r)]:[]),o!==s){let a=c.querySelector("[aria-modal=true]"),x=(E=a==null?void 0:a.querySelector("[role=radio], [data-ckf-id=go], textarea"))!=null?E:null;x==null||x.focus(),!o&&g&&(g.focus(),g=null);}else l&&((M=c.querySelector(`[data-ckf-id="${CSS.escape(l)}"]`))==null||M.focus());s=o;},k=r=>{if(r.key!=="Escape")return;let o=n.view();(o==null?void 0:o.type)==="cancel"?n.cancel.close():(o==null?void 0:o.type)==="message"?n.message.dismiss():(o==null?void 0:o.type)==="step"&&o.step.kind==="modal"&&n.onboarding.dismiss();},R=()=>{w&&b(w.float,w.ring,w.target);};document.addEventListener("keydown",k),window.addEventListener("resize",R),window.addEventListener("scroll",R,{passive:true,capture:true});let I=n.subscribe(F);return F(),()=>{I(),document.removeEventListener("keydown",k),window.removeEventListener("resize",R),window.removeEventListener("scroll",R,{capture:true}),c.remove();}},K=(n,e={})=>{if(typeof document=="undefined")return ()=>{};let c=H(e),s="",g=null,u=()=>{var h,N,F;let p=n.view(),i=p?`${p.paywall.id}:${p.index}`:"";if(i&&!s&&document.activeElement instanceof HTMLElement&&(g=document.activeElement),!p){c.replaceChildren(),c.removeAttribute("data-still"),s&&(g==null||g.focus()),s="";return}s&&c.setAttribute("data-still","");let f=p.page,b=f.kind==="plans"?t("div",{className:"ckf-opts",role:"radiogroup","aria-label":"Plans"},...p.plans.map(k=>t("button",{type:"button",role:"radio",className:"ckf-opt ckf-plan","aria-checked":String(p.plan===k.id),"data-ckf-id":`p:${k.id}`,onClick:()=>n.pick(k.id)},t("span",{className:"ckf-radio"}),t("span",{},k.name,k.note?t("span",{className:"ckf-small"},k.note):false),t("b",{},k.price)))):false;c.replaceChildren(t("div",{className:"ckf-scrim",onClick:k=>k.target===k.currentTarget&&n.close()},t("div",{className:"ckf-card ckf-pw",role:"dialog","aria-modal":"true","aria-labelledby":"ckf-title"},t("div",{className:"ckf-top"},V(p.index+1,p.total),$("Close",n.close)),t("h2",{className:"ckf-h",id:"ckf-title"},f.title),f.body?t("p",{className:"ckf-p"},f.body):false,f.kind==="benefits"&&((h=f.items)!=null&&h.length)?t("div",{className:"ckf-opts"},...f.items.map(k=>t("p",{className:"ckf-li"},k))):false,f.kind==="proof"&&((N=f.quotes)!=null&&N.length)?t("div",{className:"ckf-opts"},...f.quotes.map(k=>t("blockquote",{className:"ckf-quote"},k.text,k.by?t("cite",{},k.by):false))):false,b,p.error?t("p",{className:"ckf-err",role:"alert"},p.error):false,t("div",{className:"ckf-btns"},P(p.busy?"One moment\u2026":f.button||"Continue","primary",n.next,{disabled:p.busy||f.kind==="plans"&&!p.plan,id:"go"}),p.index?P("Back","quiet",n.back,{disabled:p.busy}):false)))),i!==s&&((F=c.querySelector("[data-ckf-id=go]"))==null||F.focus()),s=i;},w=p=>{n.view()&&p.key==="Escape"&&n.close();};document.addEventListener("keydown",w);let C=n.subscribe(u);return u(),()=>{C(),document.removeEventListener("keydown",w),c.remove();}};var Y=n=>{let e=2166136261;for(let c=0;c<n.length;c++)e^=n.charCodeAt(c),e=Math.imul(e,16777619);return (e>>>0)/4294967296};var q=(n,e)=>{var u,w,C,p;let c=(w=(u=n.variants[0])==null?void 0:u.id)!=null?w:"control";if(n.winner)return n.winner;if(n.status!=="running")return c;let s=Y(`${n.key}:${e}`),g=0;for(let i of n.variants)if(g+=i.weight,s<g)return i.id;return (p=(C=n.variants[n.variants.length-1])==null?void 0:C.id)!=null?p:c};var _="$paywall",B=(n,e,c)=>{var w,C;let s=e.experiment?n==null?void 0:n.experiments.find(p=>p.key===e.experiment):void 0,g=s?q(s,c):(w=e.variants[0])==null?void 0:w.id,u=(C=e.variants.find(p=>p.id===g))!=null?C:e.variants[0];return u?{variant:u.id,pages:u.pages,test:(s==null?void 0:s.status)==="running"&&!s.winner?s.key:void 0}:null},z=n=>{let e=null,c=new Set,s=()=>{for(let i of [...c])try{i();}catch(f){}},g=()=>e?{paywall:e.w.id,placement:e.w.placement,variant:e.variant}:{},u=()=>{e&&!e.seen.has(e.index)&&(e.seen.add(e.index),n.track(_,{...g(),page:e.pages[e.index].id,index:e.index,action:"page"})),s();},w=i=>{var b,h;let f=e;f&&(e=null,i.outcome==="closed"&&((h=(b=f.options).onClose)==null||h.call(b,i)),f.resolve(i),s());},C=i=>{var f;return {outcome:i,index:(f=e==null?void 0:e.index)!=null?f:0,...e?{paywall:e.w.id,variant:e.variant}:{},...i!=="closed"&&e?{plan:e.plans.find(b=>b.id===e.plan)}:{}}},p=()=>{let i=e,f=i==null?void 0:i.plans.find(b=>b.id===i.plan);if(!(!i||!f||i.busy)){if(!i.options.onPurchase)return w(C("chosen"));i.busy=true,i.error=void 0,s(),Promise.resolve().then(()=>{var b,h;return (h=(b=i.options).onPurchase)==null?void 0:h.call(b,f)}).then(b=>{if(i.busy=false,e===i){if(b===false)return s();n.track(_,{...g(),page:i.pages[i.index].id,index:i.index,plan:f.id,action:"purchased"}),w(C("purchased"));}},()=>{i.busy=false,i.error="The purchase did not go through. Please try again.",s();});}};return {open:(i,f={})=>{var o,l,d;if(e)return e.promise;let b=n.config(),h=(o=b==null?void 0:b.paywalls)==null?void 0:o.find(m=>m.placement===i),N=h?B(b,h,n.distinctId()):null;if(!h||!(N!=null&&N.pages.length))return Promise.resolve({outcome:"closed",index:0});let F=new Map(((l=f.plans)!=null?l:[]).map(m=>[m.id,m])),k=h.plans.map(m=>{var y;return (y=F.get(m.id))!=null?y:m}),R=()=>{},I=new Promise(m=>R=m),r=k.some(m=>m.id===h.highlight)?h.highlight:(d=k[0])==null?void 0:d.id;return e={w:h,variant:N.variant,pages:N.pages,plans:k,index:0,plan:r,busy:false,seen:new Set,options:f,resolve:R,promise:I},N.test&&n.track("$exposure",{experiment:N.test,variant:N.variant}),u(),I},view:()=>e?{paywall:e.w,variant:e.variant,page:e.pages[e.index],index:e.index,total:e.pages.length,last:e.index===e.pages.length-1,plans:e.plans,plan:e.plan,busy:e.busy,error:e.error}:null,subscribe:i=>(c.add(i),()=>c.delete(i)),next:()=>{if(!(!e||e.busy)){if(e.pages[e.index].kind==="plans"||e.index===e.pages.length-1)return p();e.index++,e.error=void 0,u();}},back:()=>{!e||e.busy||!e.index||(e.index--,e.error=void 0,s());},pick:i=>{!e||e.busy||!e.plans.some(f=>f.id===i)||(e.plan=i,e.error=void 0,s());},close:()=>{!e||e.busy||(n.track(_,{...g(),index:e.index,action:"closed"}),w(C("closed")));}}};var J=(n,e)=>{let c=z(n);return K(c,e),c};window.__mrrocketFlows={mountFlows:j,paywallHost:J};})();
|