@mrrocket/sdk 0.16.0 → 0.18.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +11 -2
  3. package/dist/api-client.d.ts +7 -1
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/browser/mrrocket-flows.js +16 -2
  6. package/dist/browser/mrrocket-messenger.js +43 -44
  7. package/dist/browser/mrrocket.js +1 -1
  8. package/dist/browser.d.ts +1 -1
  9. package/dist/browser.js +2 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/client.d.ts +23 -3
  12. package/dist/client.js +15 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/context.d.ts +3 -1
  15. package/dist/context.js.map +1 -1
  16. package/dist/core.d.ts +3 -1
  17. package/dist/core.js +1 -1
  18. package/dist/core.js.map +1 -1
  19. package/dist/finish-web.d.ts +18 -0
  20. package/dist/finish-web.js +139 -0
  21. package/dist/finish-web.js.map +1 -0
  22. package/dist/flows-ui.d.ts +1 -10
  23. package/dist/flows-ui.js +165 -16
  24. package/dist/flows-ui.js.map +1 -1
  25. package/dist/flows-web.d.ts +8 -1
  26. package/dist/flows-web.js +8 -0
  27. package/dist/flows-web.js.map +1 -1
  28. package/dist/flows.d.ts +203 -5
  29. package/dist/flows.js +306 -15
  30. package/dist/flows.js.map +1 -1
  31. package/dist/icons.d.ts +24 -0
  32. package/dist/icons.js +61 -0
  33. package/dist/icons.js.map +1 -0
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.js +10 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/messenger-ui.d.ts +10 -2
  38. package/dist/messenger-ui.js +134 -98
  39. package/dist/messenger-ui.js.map +1 -1
  40. package/dist/onboarding-state.d.ts +23 -0
  41. package/dist/onboarding-state.js +43 -0
  42. package/dist/onboarding-state.js.map +1 -0
  43. package/dist/react-native/index.d.cts +216 -15
  44. package/dist/react-native/index.d.ts +216 -15
  45. package/dist/react-native/index.js +880 -270
  46. package/dist/react-native/index.js.map +1 -1
  47. package/dist/styles-css.d.ts +1 -1
  48. package/dist/styles-css.js +40 -41
  49. package/dist/styles-css.js.map +1 -1
  50. package/package.json +1 -1
@@ -48,10 +48,10 @@ __export(native_exports, {
48
48
  useVariant: () => useVariant
49
49
  });
50
50
  module.exports = __toCommonJS(native_exports);
51
- var import_react3 = require("react");
51
+ var import_react4 = require("react");
52
52
 
53
53
  // src/core.ts
54
- var SDK_VERSION = "0.16.0";
54
+ var SDK_VERSION = "0.18.0";
55
55
  var propOf = (person, name) => {
56
56
  const want = name.trim().toLowerCase();
57
57
  for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
@@ -173,6 +173,46 @@ var textChanges = (config, distinctId, preview) => {
173
173
  };
174
174
  var newDistinctId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
175
175
 
176
+ // src/onboarding-state.ts
177
+ var import_react = require("react");
178
+ var none = [];
179
+ var useOnboardingItem = (flows, id, start) => {
180
+ const [state, setState] = (0, import_react.useState)(() => flows && id ? flows.onboarding.item(id) : null);
181
+ (0, import_react.useEffect)(() => {
182
+ if (!flows || !id) {
183
+ setState(null);
184
+ return;
185
+ }
186
+ const up = () => {
187
+ const s = flows.onboarding.item(id);
188
+ setState(s);
189
+ if (s && !s.done && !s.dismissed) flows.onboarding.seen(id);
190
+ };
191
+ up();
192
+ return flows.subscribe(up);
193
+ }, [flows, id]);
194
+ return (0, import_react.useMemo)(
195
+ () => ({
196
+ item: state?.item ?? null,
197
+ steps: state?.steps ?? none,
198
+ done: !!state?.done,
199
+ dismissed: !!state?.dismissed,
200
+ eligible: !!state?.eligible,
201
+ complete: (stepId) => {
202
+ if (flows && id) flows.onboarding.complete(id, stepId);
203
+ },
204
+ dismiss: () => {
205
+ if (flows && id) flows.onboarding.dismissItem(id);
206
+ },
207
+ answer: (question, value) => {
208
+ if (flows && id) flows.onboarding.answerItem(id, question, value);
209
+ },
210
+ start
211
+ }),
212
+ [state, flows, id, start]
213
+ );
214
+ };
215
+
176
216
  // src/identity.ts
177
217
  var UNSIGNED_HELP = "401: sign the user id on your server with userHash(id) from @mrrocket/sdk/next/server and pass identify(id, props, { hash }).";
178
218
  var warnUnsigned = (status, log = (m) => console.warn(m)) => {
@@ -242,9 +282,9 @@ var call = async (url, init, signal) => {
242
282
  };
243
283
  var createApi = (c) => {
244
284
  const base = `${c.host}/api/sdk`;
245
- const who = { key: c.apiKey, distinctId: c.distinctId, user: c.user, userHash: c.userHash };
285
+ const who2 = { key: c.apiKey, distinctId: c.distinctId, user: c.user, userHash: c.userHash };
246
286
  const me = { key: c.apiKey, distinctId: c.distinctId, userHash: c.userHash };
247
- const post = (path, body, signal) => call(`${base}${path}`, { method: "POST", body: JSON.stringify({ ...who, ...body }) }, signal);
287
+ const post = (path, body, signal) => call(`${base}${path}`, { method: "POST", body: JSON.stringify({ ...who2, ...body }) }, signal);
248
288
  return {
249
289
  list: (o, signal) => call(`${base}/feedback?${qs({ ...me, board: o.board, sort: o.sort, q: o.q })}`, void 0, signal),
250
290
  post: (id, signal) => call(`${base}/feedback/${encodeURIComponent(id)}?${qs(me)}`, void 0, signal),
@@ -358,6 +398,39 @@ var matchesTrigger = (trigger, event) => {
358
398
  return t === e || SIGNUP.has(t) && SIGNUP.has(e);
359
399
  };
360
400
  var startsOnLoad = (trigger) => /^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(trigger.trim());
401
+ var isPageTrigger = (trigger) => trigger.trim().startsWith("/");
402
+ var NEEDS_NEWER = "Needs tracking code 0.18.0";
403
+ var momentOf = (f) => f.when ?? f.trigger;
404
+ var manual = (trigger) => eventSlug(trigger) === "from_a_checklist" || eventSlug(trigger) === "from_your_code" || eventSlug(trigger) === eventSlug(NEEDS_NEWER);
405
+ var AFTER_PREFIX = "after:";
406
+ var afterOf = (trigger) => {
407
+ const t = (trigger ?? "").trim();
408
+ return t.toLowerCase().startsWith(AFTER_PREFIX) ? t.slice(AFTER_PREFIX.length).trim() || null : null;
409
+ };
410
+ var opensOf = (step) => step.opens ?? (step.startsTour ? { kind: "tour", id: step.startsTour } : void 0);
411
+ var answerOk = (q, value) => {
412
+ if (!q || value == null) return false;
413
+ if (q.type === "choice") return (q.options ?? []).some((o) => o.id === value);
414
+ return value.trim().length > 0 && value.length <= 500;
415
+ };
416
+ var answerLabel = (q, value) => q?.type === "choice" ? q.options?.find((o) => o.id === value)?.label ?? value : value.trim();
417
+ var videoEmbed = (url) => {
418
+ const u = (url ?? "").trim();
419
+ if (!/^https?:\/\//i.test(u)) return null;
420
+ let m = u.match(/^https?:\/\/(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/|shorts\/|live\/)|youtu\.be\/|youtube-nocookie\.com\/embed\/)([\w-]{6,20})/i);
421
+ if (m) return { kind: "iframe", src: `https://www.youtube-nocookie.com/embed/${m[1]}?rel=0` };
422
+ m = u.match(/^https?:\/\/(?:www\.|player\.)?vimeo\.com\/(?:video\/)?(\d{4,12})/i);
423
+ if (m) return { kind: "iframe", src: `https://player.vimeo.com/video/${m[1]}?dnt=1` };
424
+ m = u.match(/^https?:\/\/(?:www\.)?loom\.com\/(?:share|embed)\/([\w-]{8,64})/i);
425
+ if (m) return { kind: "iframe", src: `https://www.loom.com/embed/${m[1]}` };
426
+ if (/\.(mp4|webm|mov|m4v)(\?|#|$)/i.test(u) || /\/media\/uploads\//.test(u)) return { kind: "file", src: u };
427
+ return null;
428
+ };
429
+ var mediaOf = (step) => {
430
+ const m = step.media;
431
+ if (!m || m.kind !== "image" && m.kind !== "video" || !/^https?:\/\//i.test(m.url ?? "")) return void 0;
432
+ return m.kind === "video" && !videoEmbed(m.url) ? void 0 : m;
433
+ };
361
434
  var runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
362
435
  var discounted = (amount, percent) => Math.round(amount * (1 - percent / 100) * 100) / 100;
363
436
  var money = (amount, currency) => {
@@ -405,7 +478,13 @@ var createFlows = (env) => {
405
478
  const seenEvents = /* @__PURE__ */ new Set();
406
479
  const now = () => env.now?.() ?? Date.now();
407
480
  let timer = null;
481
+ let returnTo = null;
482
+ const answers = /* @__PURE__ */ new Map();
483
+ const drafts = /* @__PURE__ */ new Map();
484
+ const celebrated = /* @__PURE__ */ new Set();
485
+ let finaleNow = null;
408
486
  const flows = () => config?.onboarding ?? [];
487
+ const drawn = () => flows().filter((f) => f.shownBy !== "code");
409
488
  const flowById = (id) => id ? flows().find((f) => f.id === id) : void 0;
410
489
  const prog = (id) => {
411
490
  let p = progress.get(id);
@@ -479,6 +558,14 @@ var createFlows = (env) => {
479
558
  };
480
559
  const keyOf = (pid) => `${PROGRESS_KEY}:${pid ?? "visitor"}`;
481
560
  const eventsKeyOf = (pid) => `${PROGRESS_KEY}:events:${pid ?? "visitor"}`;
561
+ const answersKeyOf = (pid) => `${PROGRESS_KEY}:answers:${pid ?? "visitor"}`;
562
+ const celebrate = (f) => {
563
+ if (!f.finish || f.finish === "none" || f.shownBy === "code" || celebrated.has(f.id)) return;
564
+ const p = progress.get(f.id);
565
+ if (!p || p.dismissed || walkLine(f, nodesOf(f), p, false).at !== "end") return;
566
+ celebrated.add(f.id);
567
+ finaleNow = { kind: f.finish, key: `${f.id}:${now()}` };
568
+ };
482
569
  const save = () => {
483
570
  dirty = false;
484
571
  const store = env.storage?.();
@@ -494,6 +581,7 @@ var createFlows = (env) => {
494
581
  };
495
582
  try {
496
583
  store.set(keyOf(env.personId()), JSON.stringify(out));
584
+ if (answers.size) store.set(answersKeyOf(env.personId()), JSON.stringify(Object.fromEntries(answers)));
497
585
  if (seenEvents.size) store.set(eventsKeyOf(env.personId()), JSON.stringify([...seenEvents].slice(-200)));
498
586
  } catch {
499
587
  }
@@ -542,7 +630,23 @@ var createFlows = (env) => {
542
630
  shown.add(`s:${i.id}`);
543
631
  env.track(ONBOARDING_EVENT, { flow: flow.id, step: i.id, kind: "checklist", action: "shown" });
544
632
  }
545
- return { type: "checklist", flow, items: items.map((s) => ({ step: s, done: p.done.has(s.id) })) };
633
+ return {
634
+ type: "checklist",
635
+ flow,
636
+ items: items.map((s) => {
637
+ const o = opensOf(s);
638
+ const t = o && (o.kind === "tour" || o.kind === "modal") && o.id !== flow.id ? flowById(o.id) : void 0;
639
+ const target = t && t.shownBy !== "code" && nodesOf(t).length ? t : void 0;
640
+ const opens = o && (o.kind === "page" ? !!o.target.trim() : o.kind === "code" || !!target) ? o : void 0;
641
+ return {
642
+ step: s,
643
+ done: p.done.has(s.id),
644
+ ...target && o?.kind === "tour" ? { tour: target } : {},
645
+ ...opens ? { opens } : {},
646
+ ...target ? { target } : {}
647
+ };
648
+ })
649
+ };
546
650
  }
547
651
  if (!shown.has(`s:${step.id}`)) {
548
652
  shown.add(`s:${step.id}`);
@@ -551,7 +655,16 @@ var createFlows = (env) => {
551
655
  const rest = line.slice(index + 1);
552
656
  const fork = rest.findIndex((n) => n.kind === "condition");
553
657
  const ahead = (fork < 0 ? rest : rest.slice(0, fork)).filter(isStep).length;
554
- return { type: "step", flow, step, index: w.before, total: w.before + 1 + ahead, last: !ahead && fork < 0 };
658
+ return {
659
+ type: "step",
660
+ flow,
661
+ step,
662
+ index: w.before,
663
+ total: w.before + 1 + ahead,
664
+ last: !ahead && fork < 0,
665
+ // 0.18.0: a question's answer so far, and whether there is one before it to go back to.
666
+ ...step.kind === "question" ? { answer: drafts.get(step.id) ?? (step.question ? answers.get(step.question.key) : void 0), back: w.before > 0 } : {}
667
+ };
555
668
  };
556
669
  const view = () => {
557
670
  if (cancel) {
@@ -576,7 +689,30 @@ var createFlows = (env) => {
576
689
  }
577
690
  const ob = onboardingView();
578
691
  if (ob) return ob;
579
- if (active && !holding) active = null;
692
+ if (active && !holding) {
693
+ const back = returnTo;
694
+ const ended2 = active;
695
+ returnTo = null;
696
+ active = null;
697
+ const list = back ? flowById(back.flow) : void 0;
698
+ if (back && list) {
699
+ const item = stepsIn(nodesOf(list)).find((s) => s.id === back.step);
700
+ const p = prog(list.id);
701
+ if (item && !item.event && !progress.get(ended2)?.dismissed && !p.done.has(item.id)) {
702
+ markDone(p, item.id);
703
+ record({ flow: list.id, step: item.id, kind: "checklist", action: "completed" });
704
+ celebrate(list);
705
+ }
706
+ active = list.id;
707
+ const again = onboardingView();
708
+ if (again) return again;
709
+ if (!holding) active = null;
710
+ } else if (known) {
711
+ const was = flowById(ended2);
712
+ const after = was ? momentOf(was) : void 0;
713
+ Promise.resolve().then(() => autoStart(after));
714
+ }
715
+ }
580
716
  const m = messages.find((x) => !handled.has(x.id));
581
717
  return m ? { type: "message", message: m } : null;
582
718
  };
@@ -601,19 +737,67 @@ var createFlows = (env) => {
601
737
  emit();
602
738
  return true;
603
739
  };
604
- const autoStart = () => {
740
+ const allowed = (f) => f.audience === "answered" ? !!f.answered && answers.get(f.answered.question) === f.answered.option : f.audience !== "signed_in" || !!env.personId();
741
+ const predecessorEnded = (id) => {
742
+ const g = flowById(id);
743
+ return !g || finished(g);
744
+ };
745
+ const due = (f, trigger = momentOf(f)) => {
746
+ const path = env.path?.();
747
+ const pred = afterOf(trigger);
748
+ if (pred) return predecessorEnded(pred);
749
+ if (manual(trigger)) return false;
750
+ if (isPageTrigger(trigger)) return path !== void 0 && pageMatches(trigger, path);
751
+ if (startsOnLoad(trigger)) return true;
752
+ for (const e of seenEvents) if (matchesTrigger(trigger, e)) return true;
753
+ return false;
754
+ };
755
+ const autoStart = (after) => {
605
756
  if (!auto || active || !known) return;
606
757
  if (!env.personId() && !env.storage?.()) return;
607
- const next = flows().find((f) => startsOnLoad(f.trigger) && nodesOf(f).length && !finished(f));
758
+ const path = env.path?.();
759
+ const same2 = after && !manual(after) ? eventSlug(after) : "";
760
+ const next = drawn().find(
761
+ (f) => (afterOf(momentOf(f)) ? predecessorEnded(afterOf(momentOf(f))) : isPageTrigger(momentOf(f)) ? path !== void 0 && pageMatches(momentOf(f), path) : startsOnLoad(momentOf(f)) || !!same2 && eventSlug(momentOf(f)) === same2) && allowed(f) && nodesOf(f).length && !finished(f)
762
+ );
608
763
  if (next) start(next);
609
764
  };
765
+ const openItem = (list, stepId, target) => {
766
+ const p = prog(target.id);
767
+ p.done.clear();
768
+ p.at.clear();
769
+ p.dismissed = false;
770
+ returnTo = { flow: list.id, step: stepId };
771
+ active = target.id;
772
+ env.track(ONBOARDING_EVENT, { flow: target.id, action: "started", from: list.id });
773
+ save();
774
+ emit();
775
+ };
776
+ const answerStep = (flow, step, raw) => {
777
+ const q = step.question;
778
+ const value = q.type === "text" ? raw.trim().slice(0, 500) : raw;
779
+ answers.set(q.key, value);
780
+ drafts.delete(step.id);
781
+ markDone(prog(flow.id), step.id);
782
+ env.track(ONBOARDING_EVENT, { flow: flow.id, step: step.id, kind: "question", action: "completed", question: q.key, answer: value, label: answerLabel(q, value) });
783
+ celebrate(flow);
784
+ save();
785
+ };
786
+ const ended = (f) => {
787
+ save();
788
+ if (finished(f)) Promise.resolve().then(() => autoStart());
789
+ emit();
790
+ };
610
791
  const refresh = () => {
611
792
  const pid = env.personId();
612
793
  if (progressFor !== null && progressFor !== pid) {
613
794
  progress.clear();
614
795
  seenEvents.clear();
796
+ answers.clear();
797
+ drafts.clear();
615
798
  handled.clear();
616
799
  active = null;
800
+ returnTo = null;
617
801
  loadedFor = void 0;
618
802
  }
619
803
  progressFor = pid;
@@ -627,6 +811,10 @@ var createFlows = (env) => {
627
811
  if (seen) {
628
812
  for (const e of JSON.parse(seen)) if (typeof e === "string") seenEvents.add(e);
629
813
  }
814
+ const said = await store.get(answersKeyOf(pid));
815
+ if (said) {
816
+ for (const [k, v] of Object.entries(JSON.parse(said))) if (typeof v === "string") answers.set(k, v);
817
+ }
630
818
  } catch {
631
819
  }
632
820
  }
@@ -637,6 +825,7 @@ var createFlows = (env) => {
637
825
  if (state && env.personId() === pid) {
638
826
  merge(state.onboarding);
639
827
  for (const e of state.events ?? []) if (typeof e === "string") seenEvents.add(eventSlug(e));
828
+ for (const [k, v] of Object.entries(state.answers ?? {})) if (typeof v === "string" && !answers.has(k)) answers.set(k, v);
640
829
  messages = (state.messages ?? []).filter((m) => m && m.id && m.title);
641
830
  }
642
831
  }
@@ -652,9 +841,9 @@ var createFlows = (env) => {
652
841
  };
653
842
  const offerAt = (c) => c.round > 1 ? { round: 2 } : c.rule >= 0 ? { rule: c.rule } : {};
654
843
  const toOffer = (c) => {
655
- const who = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };
844
+ const who2 = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };
656
845
  const rules = c.flow.rules?.[c.reason] ?? [];
657
- c.rule = rules.findIndex((r) => ruleMatches(r, who));
846
+ c.rule = rules.findIndex((r) => ruleMatches(r, who2));
658
847
  c.offer = c.rule < 0 ? c.flow.offers[c.reason] : rules[c.rule].offer ?? void 0;
659
848
  c.round = 1;
660
849
  c.screen = c.offer ? "offer" : "confirm";
@@ -723,33 +912,40 @@ var createFlows = (env) => {
723
912
  },
724
913
  refresh,
725
914
  onEvent: (name) => {
915
+ if (name === "$pageview") autoStart();
726
916
  if (!name || name.startsWith("$")) return;
727
917
  const slug = eventSlug(name);
728
918
  const fresh = !!slug && !seenEvents.has(slug);
729
919
  if (fresh) seenEvents.add(slug);
730
920
  const flow = flowById(active);
731
921
  let ticked = false;
732
- if (flow) {
733
- const p = prog(flow.id);
734
- for (const s of stepsIn(nodesOf(flow)))
922
+ const lists = [...flow ? [flow] : [], ...flows().filter((f) => f.shownBy === "code" && f.id !== flow?.id)];
923
+ for (const l of lists) {
924
+ const p = prog(l.id);
925
+ let here = false;
926
+ for (const s of stepsIn(nodesOf(l)))
735
927
  if (s.kind === "checklist" && s.event && !p.done.has(s.id) && matchesTrigger(s.event, name)) {
736
928
  markDone(p, s.id);
737
- ticked = true;
738
- env.track(ONBOARDING_EVENT, { flow: flow.id, step: s.id, kind: s.kind, action: "completed" });
929
+ here = true;
930
+ env.track(ONBOARDING_EVENT, { flow: l.id, step: s.id, kind: s.kind, action: "completed" });
739
931
  }
932
+ if (here) {
933
+ ticked = true;
934
+ celebrate(l);
935
+ }
740
936
  }
741
- if (ticked || fresh && flow) {
937
+ if (ticked || fresh && (flow || lists.length)) {
742
938
  save();
743
939
  emit();
744
940
  } else if (fresh) save();
745
941
  if (!auto || !env.personId() && !env.storage?.()) return;
746
- const next = flows().find((f) => nodesOf(f).length && matchesTrigger(f.trigger, name) && !finished(f));
942
+ const next = drawn().find((f) => nodesOf(f).length && !manual(momentOf(f)) && matchesTrigger(momentOf(f), name) && allowed(f) && !finished(f));
747
943
  if (next) start(next);
748
944
  },
749
945
  openCancelFlow,
750
946
  startOnboarding: (id) => {
751
- const f = id ? flowById(id) : flows().find((x) => nodesOf(x).length && !finished(x));
752
- if (!f || !nodesOf(f).length) return false;
947
+ const f = id ? flowById(id) : drawn().find((x) => nodesOf(x).length && !manual(momentOf(x)) && !afterOf(momentOf(x)) && !finished(x));
948
+ if (!f || !nodesOf(f).length || f.shownBy === "code") return false;
753
949
  const p = prog(f.id);
754
950
  p.dismissed = false;
755
951
  return start(f);
@@ -840,10 +1036,21 @@ var createFlows = (env) => {
840
1036
  next: () => {
841
1037
  const v = onboardingView();
842
1038
  if (v?.type !== "step") return;
1039
+ if (v.step.kind === "question") {
1040
+ const q = v.step.question;
1041
+ const a = drafts.get(v.step.id) ?? (q ? answers.get(q.key) : void 0);
1042
+ if (!q || !answerOk(q, a)) return;
1043
+ answerStep(v.flow, v.step, a);
1044
+ emit();
1045
+ return;
1046
+ }
843
1047
  markDone(prog(v.flow.id), v.step.id);
844
1048
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: v.step.kind, action: "completed" });
1049
+ celebrate(v.flow);
845
1050
  save();
846
1051
  emit();
1052
+ const url = v.step.kind === "modal" ? safeUrl(v.step.url) : void 0;
1053
+ if (url) env.openUrl?.(url);
847
1054
  },
848
1055
  check: (stepId) => {
849
1056
  const v = onboardingView();
@@ -852,6 +1059,7 @@ var createFlows = (env) => {
852
1059
  if (!item || item.done) return;
853
1060
  markDone(prog(v.flow.id), stepId);
854
1061
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "completed" });
1062
+ celebrate(v.flow);
855
1063
  save();
856
1064
  emit();
857
1065
  },
@@ -860,11 +1068,120 @@ var createFlows = (env) => {
860
1068
  if (!v || v.type !== "step" && v.type !== "checklist") return;
861
1069
  prog(v.flow.id).dismissed = true;
862
1070
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, ...v.type === "step" ? { step: v.step.id } : {}, action: "dismissed" });
863
- active = null;
1071
+ active = returnTo && returnTo.flow !== v.flow.id && flowById(returnTo.flow) ? returnTo.flow : null;
1072
+ returnTo = null;
1073
+ save();
1074
+ emit();
1075
+ },
1076
+ startTour: (stepId) => {
1077
+ const v = onboardingView();
1078
+ if (v?.type !== "checklist") return false;
1079
+ const target = v.items.find((i) => i.step.id === stepId)?.target;
1080
+ if (!target) return false;
1081
+ openItem(v.flow, stepId, target);
1082
+ return true;
1083
+ },
1084
+ open: (stepId) => {
1085
+ const v = onboardingView();
1086
+ if (v?.type !== "checklist") return false;
1087
+ const it = v.items.find((i) => i.step.id === stepId);
1088
+ const o = it?.opens;
1089
+ if (!it || !o) return false;
1090
+ env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "opened", opens: o.kind });
1091
+ if ((o.kind === "tour" || o.kind === "modal") && it.target) {
1092
+ openItem(v.flow, stepId, it.target);
1093
+ return true;
1094
+ }
1095
+ if (!it.done && !it.step.event) {
1096
+ markDone(prog(v.flow.id), stepId);
1097
+ env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "completed" });
1098
+ celebrate(v.flow);
1099
+ }
864
1100
  save();
865
1101
  emit();
1102
+ if (o.kind === "page") env.navigate?.(o.target.trim());
1103
+ else env.task?.(stepId, v.flow);
1104
+ return true;
1105
+ },
1106
+ answer: (value) => {
1107
+ const v = onboardingView();
1108
+ if (v?.type !== "step" || v.step.kind !== "question") return;
1109
+ drafts.set(v.step.id, String(value).slice(0, 500));
1110
+ emit();
1111
+ },
1112
+ back: () => {
1113
+ const v = onboardingView();
1114
+ if (v?.type !== "step" || v.step.kind !== "question" || !v.back) return;
1115
+ const line = stepsIn(nodesOf(v.flow));
1116
+ const at = line.findIndex((s) => s.id === v.step.id);
1117
+ const p = prog(v.flow.id);
1118
+ for (let i = at - 1; i >= 0; i--)
1119
+ if (p.done.has(line[i].id)) {
1120
+ p.done.delete(line[i].id);
1121
+ p.at.delete(line[i].id);
1122
+ break;
1123
+ }
1124
+ save();
1125
+ emit();
1126
+ },
1127
+ item: (id) => {
1128
+ const f = flowById(id);
1129
+ if (!f) return null;
1130
+ const p = progress.get(f.id);
1131
+ const steps = stepsIn(nodesOf(f)).map((s) => {
1132
+ const a = s.question ? answers.get(s.question.key) : void 0;
1133
+ return { ...s, done: !!p?.done.has(s.id), ...a !== void 0 ? { answer: a } : {} };
1134
+ });
1135
+ const done = steps.length > 0 && steps.every((s) => s.done);
1136
+ const dismissed = !!p?.dismissed;
1137
+ return { item: f, steps, done, dismissed, eligible: !done && !dismissed && allowed(f) && due(f) };
1138
+ },
1139
+ seen: (id) => {
1140
+ const f = flowById(id);
1141
+ if (!f || f.shownBy !== "code" || !known) return;
1142
+ if (!shown.has(`f:${f.id}`)) {
1143
+ shown.add(`f:${f.id}`);
1144
+ env.track(ONBOARDING_EVENT, { flow: f.id, action: "started" });
1145
+ }
1146
+ const p = progress.get(f.id);
1147
+ for (const s of stepsIn(nodesOf(f)))
1148
+ if (!p?.done.has(s.id) && !shown.has(`s:${s.id}`)) {
1149
+ shown.add(`s:${s.id}`);
1150
+ env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: "shown" });
1151
+ if (s.kind !== "checklist") break;
1152
+ }
1153
+ },
1154
+ complete: (id, stepId) => {
1155
+ const f = flowById(id);
1156
+ const s = f && stepsIn(nodesOf(f)).find((x) => x.id === stepId);
1157
+ if (!f || !s || s.kind === "question") return;
1158
+ const p = prog(f.id);
1159
+ if (p.done.has(s.id)) return;
1160
+ markDone(p, s.id);
1161
+ env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: "completed" });
1162
+ ended(f);
1163
+ },
1164
+ dismissItem: (id) => {
1165
+ const f = flowById(id);
1166
+ if (!f) return;
1167
+ const p = prog(f.id);
1168
+ if (p.dismissed) return;
1169
+ p.dismissed = true;
1170
+ env.track(ONBOARDING_EVENT, { flow: f.id, action: "dismissed" });
1171
+ if (active === f.id) active = null;
1172
+ ended(f);
1173
+ },
1174
+ answerItem: (id, question, value) => {
1175
+ const f = flowById(id);
1176
+ const s = f && stepsIn(nodesOf(f)).find((x) => x.kind === "question" && (x.id === question || x.question?.key === question));
1177
+ if (!f || !s?.question || !answerOk(s.question, value)) return;
1178
+ answerStep(f, s, value);
1179
+ ended(f);
866
1180
  }
867
1181
  },
1182
+ finale: () => finaleNow,
1183
+ accent: () => config?.onboardingAccent,
1184
+ answers: () => Object.fromEntries(answers),
868
1185
  message: {
869
1186
  click: () => {
870
1187
  const v = view();
@@ -1298,7 +1615,14 @@ var createClient = (options, deps) => {
1298
1615
  set: (k, v) => void set(k, v)
1299
1616
  } : null,
1300
1617
  openUrl: deps.openUrl,
1301
- fetchInApp: (id) => fetchInApp(id)
1618
+ fetchInApp: (id) => fetchInApp(id),
1619
+ // 0.18.0: a task's page or screen goes to the app's navigator; without one only a link with a scheme opens.
1620
+ navigate: (target) => {
1621
+ if (options.onNavigate) return options.onNavigate(target);
1622
+ if (/^[a-z][a-z0-9+.-]*:/i.test(target) && !/^(javascript|data|vbscript):/i.test(target)) deps.openUrl?.(target);
1623
+ else warn(`A checklist task opens "${target}": pass onNavigate to MRRocketProvider to open screens and paths.`);
1624
+ },
1625
+ task: (taskId, item) => options.onTask?.(taskId, item)
1302
1626
  });
1303
1627
  const refreshFlows = () => {
1304
1628
  if (!isReady || closed) return;
@@ -1474,7 +1798,7 @@ var createClient = (options, deps) => {
1474
1798
  if (errorsSeen.has(mark)) return;
1475
1799
  errorsSeen.add(mark);
1476
1800
  errorsSent++;
1477
- const who = sendId();
1801
+ const who2 = sendId();
1478
1802
  errorQueue.push({
1479
1803
  ...d,
1480
1804
  mechanism,
@@ -1483,7 +1807,7 @@ var createClient = (options, deps) => {
1483
1807
  ts: now(),
1484
1808
  crumbs: crumbs.slice(),
1485
1809
  ...release ? { release } : {},
1486
- ...who ? { distinctId: who } : {},
1810
+ ...who2 ? { distinctId: who2 } : {},
1487
1811
  ...session ? { sessionId: session.id } : {}
1488
1812
  });
1489
1813
  recorder?.mark({ kind: "error", path: currentScreen ?? "/", label: clip2(d.message, 120) });
@@ -1598,20 +1922,20 @@ var createClient = (options, deps) => {
1598
1922
  await loadedNow();
1599
1923
  if (!apiKey || consent === "off") return { status: "error", reason: !apiKey ? "No public key." : "The consent is off." };
1600
1924
  if (!identified) return { status: "not-signed-in" };
1601
- const who = identified;
1602
- const userHash = hashOf(who);
1925
+ const who2 = identified;
1926
+ const userHash = hashOf(who2);
1603
1927
  try {
1604
1928
  const permission = await deps.push.permission(o.ask !== false);
1605
1929
  if (permission !== "granted") return { status: permission === "denied" ? "denied" : "not-asked" };
1606
1930
  const token = await deps.push.token(o.projectId);
1607
1931
  if (!token) return { status: "unavailable", reason: "This device has no push token (a simulator, or no EAS project id)." };
1608
- const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId: who, userHash, platform: "expo", token, app: context }));
1932
+ const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId: who2, userHash, platform: "expo", token, app: context }));
1609
1933
  if (status < 200 || status >= 300) {
1610
1934
  warn(`Push registration was rejected with ${status || "no answer"}. Check the public key and the host.`);
1611
1935
  return { status: "error", reason: `MRRocket answered ${status || "nothing"}.` };
1612
1936
  }
1613
1937
  pushToken = token;
1614
- pushFor = who;
1938
+ pushFor = who2;
1615
1939
  pushHash = userHash;
1616
1940
  listenForOpens();
1617
1941
  debug("Registered for push.");
@@ -1936,30 +2260,216 @@ var activeRouteName = (state) => {
1936
2260
  };
1937
2261
 
1938
2262
  // src/native/flows-view.ts
1939
- var import_react = require("react");
2263
+ var import_react2 = require("react");
2264
+
2265
+ // src/icons.ts
2266
+ var FLOW_ICONS = {
2267
+ check: { label: "Check", paths: ["M5 12.5l4.5 4.5L19 7.5"], emoji: "\u2705" },
2268
+ star: { label: "Star", paths: ["M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"], emoji: "\u2B50" },
2269
+ rocket: {
2270
+ label: "Rocket",
2271
+ paths: ["M12 3c3 2 4.5 5.5 4 10l-2 3h-4l-2-3c-.5-4.5 1-8 4-10z", "M8 13.5l-3 3 3.5.5", "M16 13.5l3 3-3.5.5", "M13.5 9.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z", "M11 19.5v1.5M13 19.5v1.5"],
2272
+ emoji: "\u{1F680}"
2273
+ },
2274
+ user: { label: "Person", paths: ["M15.5 8a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z", "M5 20c.8-3.5 3.6-5.5 7-5.5s6.2 2 7 5.5"], emoji: "\u{1F464}" },
2275
+ users: {
2276
+ label: "Team",
2277
+ paths: ["M13 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z", "M3.5 19c.6-3 3-4.8 6.5-4.8s5.9 1.8 6.5 4.8", "M16 5.2a3 3 0 0 1 0 5.6", "M18 14.6c1.5.6 2.4 2 2.7 4.4"],
2278
+ emoji: "\u{1F465}"
2279
+ },
2280
+ settings: {
2281
+ label: "Settings",
2282
+ paths: ["M12 2.8v2.4M12 18.8v2.4M2.8 12h2.4M18.8 12h2.4M5.5 5.5l1.7 1.7M16.8 16.8l1.7 1.7M5.5 18.5l1.7-1.7M16.8 7.2l1.7-1.7", "M17 12a5 5 0 1 1-10 0 5 5 0 0 1 10 0z", "M14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"],
2283
+ emoji: "\u2699\uFE0F"
2284
+ },
2285
+ bell: { label: "Bell", paths: ["M6 16v-5a6 6 0 0 1 12 0v5l1.5 2h-15z", "M10 20.5a2 2 0 0 0 4 0"], emoji: "\u{1F514}" },
2286
+ mail: { label: "Mail", paths: ["M4 6h16v12H4z", "M4 7l8 6 8-6"], emoji: "\u2709\uFE0F" },
2287
+ chat: { label: "Chat", paths: ["M4 5h16v11H9l-5 4z"], emoji: "\u{1F4AC}" },
2288
+ heart: { label: "Heart", paths: ["M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z"], emoji: "\u2764\uFE0F" },
2289
+ flag: { label: "Flag", paths: ["M5 21V4", "M5 4h11l-2 4 2 4H5"], emoji: "\u{1F6A9}" },
2290
+ bolt: { label: "Bolt", paths: ["M13 3L5 13.5h6L10 21l8-10.5h-6z"], emoji: "\u26A1" },
2291
+ book: { label: "Book", paths: ["M4 5.5C6.5 4 9.5 4 12 6c2.5-2 5.5-2 8-.5V19c-2.5-1.5-5.5-1.5-8 .5-2.5-2-5.5-2-8-.5z", "M12 6v13.5"], emoji: "\u{1F4D6}" },
2292
+ calendar: { label: "Calendar", paths: ["M4 6h16v14H4z", "M4 10h16", "M8 3.5v4M16 3.5v4"], emoji: "\u{1F4C5}" },
2293
+ camera: { label: "Camera", paths: ["M4 8h3.5L9 5.5h6L16.5 8H20v11H4z", "M15.5 13a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z"], emoji: "\u{1F4F7}" },
2294
+ cart: { label: "Cart", paths: ["M3.5 4.5H6l2 10h10l2-7H7", "M9.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z", "M18.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"], emoji: "\u{1F6D2}" },
2295
+ chart: { label: "Chart", paths: ["M4 20h16", "M7 16v-4M12 16V8M17 16V9"], emoji: "\u{1F4CA}" },
2296
+ gift: { label: "Gift", paths: ["M4 10h16v10H4z", "M3 7h18v3H3z", "M12 7v13", "M12 7c-1.5-3-5-3-5-1s3.5 1 5 1z", "M12 7c1.5-3 5-3 5-1s-3.5 1-5 1z"], emoji: "\u{1F381}" },
2297
+ home: { label: "Home", paths: ["M4 11l8-7 8 7", "M6 9.5V20h12V9.5", "M10 20v-6h4v6"], emoji: "\u{1F3E0}" },
2298
+ link: { label: "Link", paths: ["M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1", "M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1"], emoji: "\u{1F517}" },
2299
+ lock: { label: "Lock", paths: ["M6 11h12v9H6z", "M8.5 11V8a3.5 3.5 0 0 1 7 0v3"], emoji: "\u{1F512}" },
2300
+ pencil: { label: "Pencil", paths: ["M4 20l1-4L16 5l3 3L8 19z", "M14 7l3 3"], emoji: "\u270F\uFE0F" },
2301
+ plus: { label: "Plus", paths: ["M12 5v14M5 12h14"], emoji: "\u2795" },
2302
+ search: { label: "Search", paths: ["M16 10.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z", "M14.5 14.5L20 20"], emoji: "\u{1F50D}" },
2303
+ upload: { label: "Upload", paths: ["M12 15V4", "M7.5 8.5L12 4l4.5 4.5", "M4 15v5h16v-5"], emoji: "\u2B06\uFE0F" },
2304
+ play: { label: "Play", paths: ["M8 5l11 7-11 7z"], emoji: "\u25B6\uFE0F" },
2305
+ sparkle: { label: "Sparkle", paths: ["M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z", "M19 16l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7z"], emoji: "\u2728" },
2306
+ globe: {
2307
+ label: "Globe",
2308
+ paths: ["M20 12a8 8 0 1 1-16 0 8 8 0 0 1 16 0z", "M4 12h16", "M12 4c2.2 2.2 3.3 4.9 3.3 8s-1.1 5.8-3.3 8c-2.2-2.2-3.3-4.9-3.3-8S9.8 6.2 12 4z"],
2309
+ emoji: "\u{1F310}"
2310
+ }
2311
+ };
2312
+ var FLOW_ICON_NAMES = Object.keys(FLOW_ICONS);
2313
+ var iconOf = (icon) => {
2314
+ if (!icon) return void 0;
2315
+ if (icon.kind === "builtin") return icon.name && FLOW_ICONS[icon.name] ? icon : void 0;
2316
+ if (icon.kind === "emoji") return icon.value && icon.value.trim() && [...icon.value.trim()].length <= 8 ? icon : void 0;
2317
+ if (icon.kind === "image") return icon.url && /^https?:\/\//i.test(icon.url) ? icon : void 0;
2318
+ return void 0;
2319
+ };
2320
+
2321
+ // src/native/flows-view.ts
1940
2322
  var RN = require("react-native");
2323
+ var svg = (() => {
2324
+ try {
2325
+ return require("react-native-svg");
2326
+ } catch {
2327
+ return null;
2328
+ }
2329
+ })();
2330
+ var iconNode = (icon, size, color) => {
2331
+ const i = iconOf(icon);
2332
+ if (!i) return null;
2333
+ if (i.kind === "image")
2334
+ return RN.Image ? (0, import_react2.createElement)(RN.Image, { source: { uri: i.url }, accessibilityElementsHidden: true, style: { width: size, height: size, borderRadius: size / 4 } }) : null;
2335
+ const Svg = svg?.Svg ?? svg?.default;
2336
+ if (i.kind === "builtin" && Svg && svg?.Path)
2337
+ return (0, import_react2.createElement)(
2338
+ Svg,
2339
+ { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
2340
+ ...FLOW_ICONS[i.name].paths.map((d, k) => (0, import_react2.createElement)(svg.Path, { key: k, d }))
2341
+ );
2342
+ return (0, import_react2.createElement)(RN.Text, { style: { fontSize: size * 0.8, lineHeight: size, textAlign: "center", width: size } }, i.kind === "emoji" ? i.value : FLOW_ICONS[i.name].emoji);
2343
+ };
2344
+ var FINALE_COLORS = ["#f59e0b", "#10b981", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899"];
2345
+ var finaleBits = (kind, accent) => {
2346
+ const rnd = (a, b) => a + Math.random() * (b - a);
2347
+ const colors = [accent, accent, ...FINALE_COLORS];
2348
+ const pick = () => colors[Math.floor(Math.random() * colors.length)];
2349
+ if (kind === "confetti")
2350
+ return Array.from({ length: 48 }, (_, i) => ({
2351
+ x: i % 2 ? 12 : 82,
2352
+ y: 92,
2353
+ dx: (i % 2 ? 1 : -1) * rnd(40, 200),
2354
+ mid: -rnd(380, 620),
2355
+ dy: -rnd(120, 300),
2356
+ size: rnd(7, 11),
2357
+ rot: rnd(-540, 540),
2358
+ color: pick(),
2359
+ delay: rnd(0, 160),
2360
+ life: rnd(1500, 2e3),
2361
+ shape: "rect"
2362
+ }));
2363
+ if (kind === "fireworks")
2364
+ return Array.from({ length: 3 }, (_, b) => {
2365
+ const x = rnd(20, 75);
2366
+ const y = rnd(18, 45);
2367
+ const color = pick();
2368
+ return Array.from({ length: 18 }, (_2, i) => {
2369
+ const a = Math.PI * 2 * i / 18;
2370
+ const r = rnd(70, 130);
2371
+ return { x, y, dx: Math.cos(a) * r, mid: Math.sin(a) * r * 0.8, dy: Math.sin(a) * r + 40, size: 5, rot: 0, color, delay: b * 320, life: rnd(900, 1200), shape: "dot" };
2372
+ });
2373
+ }).flat();
2374
+ return Array.from({ length: 22 }, () => ({
2375
+ x: rnd(6, 88),
2376
+ y: rnd(20, 80),
2377
+ dx: 0,
2378
+ mid: -rnd(10, 25),
2379
+ dy: -rnd(30, 60),
2380
+ size: rnd(16, 28),
2381
+ rot: rnd(-40, 40),
2382
+ color: pick(),
2383
+ delay: rnd(0, 600),
2384
+ life: rnd(900, 1300),
2385
+ shape: "star"
2386
+ }));
2387
+ };
2388
+ var Finale = ({ kind, accent }) => {
2389
+ const A = RN.Animated;
2390
+ const [bits] = (0, import_react2.useState)(() => finaleBits(kind, accent));
2391
+ const [values] = (0, import_react2.useState)(() => A ? bits.map(() => new A.Value(0)) : []);
2392
+ const [on, setOn] = (0, import_react2.useState)(!!A);
2393
+ (0, import_react2.useEffect)(() => {
2394
+ if (!A) return;
2395
+ let stopped = false;
2396
+ let run = null;
2397
+ const go = () => {
2398
+ if (stopped) return;
2399
+ run = A.parallel(values.map((v, i) => A.timing(v, { toValue: 1, duration: bits[i].life, delay: bits[i].delay, useNativeDriver: true })));
2400
+ run.start(() => !stopped && setOn(false));
2401
+ };
2402
+ Promise.resolve(RN.AccessibilityInfo?.isReduceMotionEnabled?.()).then((reduce) => reduce ? !stopped && setOn(false) : go()).catch(go);
2403
+ return () => {
2404
+ stopped = true;
2405
+ run?.stop();
2406
+ };
2407
+ }, [A, bits, values]);
2408
+ if (!on || !A) return null;
2409
+ return (0, import_react2.createElement)(
2410
+ RN.View,
2411
+ { pointerEvents: "none", accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { position: "absolute", left: 0, right: 0, top: 0, bottom: 0 } },
2412
+ ...bits.map((b, i) => {
2413
+ const t = values[i];
2414
+ const fade = t.interpolate({ inputRange: [0, 0.7, 1], outputRange: [1, 1, 0] });
2415
+ const scale = b.shape === "star" ? t.interpolate({ inputRange: [0, 0.25, 1], outputRange: [0.2, 1.15, 0.6] }) : 1;
2416
+ return (0, import_react2.createElement)(
2417
+ A.View,
2418
+ {
2419
+ key: i,
2420
+ style: {
2421
+ position: "absolute",
2422
+ left: `${b.x}%`,
2423
+ top: `${b.y}%`,
2424
+ width: b.size,
2425
+ height: b.shape === "rect" ? b.size * 0.45 : b.size,
2426
+ borderRadius: b.shape === "dot" ? b.size / 2 : 2,
2427
+ backgroundColor: b.shape === "star" ? "transparent" : b.color,
2428
+ alignItems: "center",
2429
+ justifyContent: "center",
2430
+ opacity: fade,
2431
+ transform: [
2432
+ { translateX: t.interpolate({ inputRange: [0, 1], outputRange: [0, b.dx] }) },
2433
+ { translateY: t.interpolate({ inputRange: [0, 0.5, 1], outputRange: [0, b.mid, b.dy] }) },
2434
+ { rotate: t.interpolate({ inputRange: [0, 1], outputRange: ["0deg", `${b.rot}deg`] }) },
2435
+ { scale }
2436
+ ]
2437
+ }
2438
+ },
2439
+ b.shape === "star" ? (0, import_react2.createElement)(RN.Text, { style: { color: b.color, fontSize: b.size, lineHeight: b.size } }, "\u2605") : null
2440
+ );
2441
+ })
2442
+ );
2443
+ };
1941
2444
  var LIGHT = { bg: "#ffffff", fg: "#1c1d20", muted: "#5d6068", soft: "#f4f5f7", line: "#e4e5e8" };
1942
2445
  var DARK = { bg: "#17181b", fg: "#f1f2f4", muted: "#a9acb3", soft: "#232428", line: "#2e2f35" };
1943
2446
  var months = (n) => `${n} ${n === 1 ? "month" : "months"}`;
1944
- var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2447
+ var MRRocketFlows = (props) => {
2448
+ const [, setTick] = (0, import_react2.useState)(0);
2449
+ (0, import_react2.useEffect)(() => props.flows.subscribe(() => setTick((t) => t + 1)), [props.flows]);
2450
+ const fin = props.flows.finale?.() ?? null;
2451
+ const accent = props.appearance?.accent ?? props.flows.accent?.() ?? "#3f76ff";
2452
+ return (0, import_react2.createElement)(import_react2.Fragment, null, (0, import_react2.createElement)(FlowsBody, props), fin ? (0, import_react2.createElement)(Finale, { key: fin.key, kind: fin.kind, accent }) : null);
2453
+ };
2454
+ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
1945
2455
  const scheme = RN.useColorScheme?.();
1946
2456
  const dark = appearance.theme === "dark" || (appearance.theme ?? "auto") === "auto" && scheme === "dark";
1947
- const p = { ...dark ? DARK : LIGHT, accent: appearance.accent ?? "#3f76ff", danger: "#d92d20" };
1948
- const [, setTick] = (0, import_react.useState)(0);
1949
- (0, import_react.useEffect)(() => flows.subscribe(() => setTick((t) => t + 1)), [flows]);
1950
- (0, import_react.useEffect)(() => paywall?.subscribe(() => setTick((t) => t + 1)), [paywall]);
2457
+ const p = { ...dark ? DARK : LIGHT, accent: appearance.accent ?? flows.accent?.() ?? "#3f76ff", danger: "#d92d20" };
2458
+ const [, setTick] = (0, import_react2.useState)(0);
2459
+ (0, import_react2.useEffect)(() => flows.subscribe(() => setTick((t) => t + 1)), [flows]);
2460
+ (0, import_react2.useEffect)(() => paywall?.subscribe(() => setTick((t) => t + 1)), [paywall]);
1951
2461
  const pw = paywall?.view() ?? null;
1952
2462
  const v = flows.view();
1953
- const [comment, setComment] = (0, import_react.useState)("");
2463
+ const [comment, setComment] = (0, import_react2.useState)("");
1954
2464
  const screen = v?.type === "cancel" ? v.screen : null;
1955
- (0, import_react.useEffect)(() => {
2465
+ (0, import_react2.useEffect)(() => {
1956
2466
  if (!screen) setComment("");
1957
2467
  }, [screen]);
1958
2468
  if (!v && !pw) return null;
1959
- const text2 = (children, style = {}) => (0, import_react.createElement)(RN.Text, { style: { color: p.fg, fontSize: 15, ...style } }, children);
2469
+ const text2 = (children, style = {}) => (0, import_react2.createElement)(RN.Text, { style: { color: p.fg, fontSize: 15, ...style } }, children);
1960
2470
  const muted = (children, style = {}) => text2(children, { color: p.muted, fontSize: 13, ...style });
1961
- const title = (children) => (0, import_react.createElement)(RN.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 18, fontWeight: "700" } }, children);
1962
- const btn = (label, kind, onPress, disabled = false) => (0, import_react.createElement)(
2471
+ const title = (children) => (0, import_react2.createElement)(RN.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 18, fontWeight: "700" } }, children);
2472
+ const btn = (label, kind, onPress, disabled = false) => (0, import_react2.createElement)(
1963
2473
  RN.Pressable,
1964
2474
  {
1965
2475
  key: label,
@@ -1983,12 +2493,12 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
1983
2493
  fontWeight: kind === "quiet" ? "500" : "600"
1984
2494
  })
1985
2495
  );
1986
- const close = (label, onPress) => (0, import_react.createElement)(
2496
+ const close = (label, onPress) => (0, import_react2.createElement)(
1987
2497
  RN.Pressable,
1988
2498
  { onPress, accessibilityRole: "button", accessibilityLabel: label, hitSlop: 12, style: { marginLeft: "auto", padding: 4 } },
1989
2499
  text2("\xD7", { fontSize: 22, color: p.muted })
1990
2500
  );
1991
- const card = (children, style = {}) => (0, import_react.createElement)(
2501
+ const card = (children, style = {}) => (0, import_react2.createElement)(
1992
2502
  RN.View,
1993
2503
  {
1994
2504
  style: {
@@ -2005,16 +2515,16 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2005
2515
  },
2006
2516
  ...children
2007
2517
  );
2008
- const sheet = (children, onClose) => (0, import_react.createElement)(
2518
+ const sheet = (children, onClose) => (0, import_react2.createElement)(
2009
2519
  RN.Modal,
2010
2520
  { visible: true, transparent: true, animationType: "fade", onRequestClose: onClose },
2011
- (0, import_react.createElement)(
2521
+ (0, import_react2.createElement)(
2012
2522
  RN.View,
2013
2523
  { style: { flex: 1, backgroundColor: "rgba(0,0,0,0.4)", justifyContent: "flex-end" } },
2014
2524
  card(children, { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, paddingBottom: 34 })
2015
2525
  )
2016
2526
  );
2017
- const input = (placeholder, value = comment, onChange = setComment) => (0, import_react.createElement)(RN.TextInput, {
2527
+ const input = (placeholder, value = comment, onChange = setComment) => (0, import_react2.createElement)(RN.TextInput, {
2018
2528
  value,
2019
2529
  onChangeText: onChange,
2020
2530
  placeholder,
@@ -2029,19 +2539,19 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2029
2539
  const host = paywall;
2030
2540
  return sheet(
2031
2541
  [
2032
- (0, import_react.createElement)(
2542
+ (0, import_react2.createElement)(
2033
2543
  RN.View,
2034
2544
  { key: "top", style: { flexDirection: "row", alignItems: "center", gap: 4 } },
2035
2545
  ...w.total > 1 ? Array.from(
2036
2546
  { length: w.total },
2037
- (_, i) => (0, import_react.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= w.index ? p.accent : p.line } })
2547
+ (_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= w.index ? p.accent : p.line } })
2038
2548
  ) : [],
2039
2549
  close("Close", host.close)
2040
2550
  ),
2041
2551
  title(pg.title),
2042
2552
  pg.body ? muted(pg.body, { fontSize: 15 }) : null,
2043
2553
  ...pg.kind === "benefits" ? (pg.items ?? []).map(
2044
- (item, i) => (0, import_react.createElement)(
2554
+ (item, i) => (0, import_react2.createElement)(
2045
2555
  RN.View,
2046
2556
  { key: `b${i}`, style: { flexDirection: "row", gap: 10, alignItems: "flex-start" } },
2047
2557
  text2("\u2713", { color: p.accent, fontWeight: "700" }),
@@ -2049,13 +2559,13 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2049
2559
  )
2050
2560
  ) : [],
2051
2561
  ...pg.kind === "proof" ? (pg.quotes ?? []).map(
2052
- (q, i) => (0, import_react.createElement)(RN.View, { key: `q${i}`, style: { backgroundColor: p.soft, borderRadius: 12, padding: 12, gap: 6 } }, text2(q.text), q.by ? muted(q.by) : null)
2562
+ (q, i) => (0, import_react2.createElement)(RN.View, { key: `q${i}`, style: { backgroundColor: p.soft, borderRadius: 12, padding: 12, gap: 6 } }, text2(q.text), q.by ? muted(q.by) : null)
2053
2563
  ) : [],
2054
- pg.kind === "plans" ? (0, import_react.createElement)(
2564
+ pg.kind === "plans" ? (0, import_react2.createElement)(
2055
2565
  RN.View,
2056
2566
  { key: "plans", accessibilityRole: "radiogroup", style: { gap: 6 } },
2057
2567
  ...w.plans.map(
2058
- (plan) => (0, import_react.createElement)(
2568
+ (plan) => (0, import_react2.createElement)(
2059
2569
  RN.Pressable,
2060
2570
  {
2061
2571
  key: plan.id,
@@ -2075,7 +2585,7 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2075
2585
  gap: 10
2076
2586
  }
2077
2587
  },
2078
- (0, import_react.createElement)(RN.View, { style: { flex: 1 } }, text2(plan.name, { fontWeight: "600" }), plan.note ? muted(plan.note) : null),
2588
+ (0, import_react2.createElement)(RN.View, { style: { flex: 1 } }, text2(plan.name, { fontWeight: "600" }), plan.note ? muted(plan.note) : null),
2079
2589
  text2(plan.price, { fontWeight: "700" })
2080
2590
  )
2081
2591
  )
@@ -2090,22 +2600,22 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2090
2600
  if (pw) return paywallSheet(pw);
2091
2601
  if (!v) return null;
2092
2602
  if (v.type === "cancel") {
2093
- const top = (0, import_react.createElement)(
2603
+ const top = (0, import_react2.createElement)(
2094
2604
  RN.View,
2095
2605
  { style: { flexDirection: "row", alignItems: "center", gap: 4 } },
2096
2606
  ...v.steps > 1 ? Array.from(
2097
2607
  { length: v.steps },
2098
- (_, i) => (0, import_react.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i < v.step ? p.accent : p.line } })
2608
+ (_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i < v.step ? p.accent : p.line } })
2099
2609
  ) : [],
2100
2610
  close("Close", flows.cancel.close)
2101
2611
  );
2102
2612
  const error = v.error ? text2(v.error, { color: p.danger, fontSize: 13 }) : null;
2103
2613
  let body = [];
2104
- const choices = (items) => (0, import_react.createElement)(
2614
+ const choices = (items) => (0, import_react2.createElement)(
2105
2615
  RN.ScrollView,
2106
2616
  { style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
2107
2617
  ...items.map(
2108
- (c) => (0, import_react.createElement)(
2618
+ (c) => (0, import_react2.createElement)(
2109
2619
  RN.Pressable,
2110
2620
  {
2111
2621
  key: c.key,
@@ -2150,7 +2660,7 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2150
2660
  body = [
2151
2661
  label ? muted(`You said: ${label}`) : null,
2152
2662
  title(o.headline),
2153
- detail ? (0, import_react.createElement)(RN.View, { style: { backgroundColor: p.soft, borderRadius: 10, padding: 12 } }, text2(detail, { fontWeight: "600" })) : null,
2663
+ detail ? (0, import_react2.createElement)(RN.View, { style: { backgroundColor: p.soft, borderRadius: 10, padding: 12 } }, text2(detail, { fontWeight: "600" })) : null,
2154
2664
  o.kind === "contact" ? input("What would make you stay?") : null,
2155
2665
  btn(v.busy ? "One moment\u2026" : offerButton(o), "primary", () => flows.cancel.accept(comment), v.busy),
2156
2666
  btn("No thanks, continue cancelling", "quiet", flows.cancel.decline, v.busy)
@@ -2165,15 +2675,92 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2165
2675
  ];
2166
2676
  return sheet([top, ...body, error], flows.cancel.close);
2167
2677
  }
2678
+ const media = (s) => {
2679
+ const m2 = mediaOf(s);
2680
+ if (!m2) return null;
2681
+ if (m2.kind === "image")
2682
+ return RN.Image ? (0, import_react2.createElement)(RN.Image, {
2683
+ key: "media",
2684
+ source: { uri: m2.url },
2685
+ accessibilityLabel: m2.alt,
2686
+ resizeMode: "cover",
2687
+ style: { width: "100%", aspectRatio: 16 / 9, borderRadius: 10, backgroundColor: p.soft }
2688
+ }) : null;
2689
+ const v2 = videoEmbed(m2.url);
2690
+ const open = v2?.kind === "file" ? v2.src : m2.url;
2691
+ return RN.Linking ? btn("\u25B6 Watch the video", "secondary", () => void RN.Linking?.openURL(open).catch(() => {
2692
+ })) : null;
2693
+ };
2694
+ if (v.type === "step" && v.step.kind === "question") {
2695
+ const s = v.step;
2696
+ const q = s.question;
2697
+ const ok = answerOk(q, v.answer);
2698
+ return sheet(
2699
+ [
2700
+ (0, import_react2.createElement)(
2701
+ RN.View,
2702
+ { key: "top", style: { flexDirection: "row", alignItems: "center", gap: 4 } },
2703
+ ...v.total > 1 ? Array.from(
2704
+ { length: v.total },
2705
+ (_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= v.index ? p.accent : p.line } })
2706
+ ) : [],
2707
+ close("Close", flows.onboarding.dismiss)
2708
+ ),
2709
+ v.index === 0 && v.flow.icon ? (0, import_react2.createElement)(RN.View, { key: "icon" }, iconNode(v.flow.icon, 36, p.accent)) : null,
2710
+ title(s.title),
2711
+ s.body ? muted(s.body, { fontSize: 15 }) : null,
2712
+ q?.type === "choice" ? (0, import_react2.createElement)(
2713
+ RN.ScrollView,
2714
+ { key: "opts", style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
2715
+ ...(q.options ?? []).map(
2716
+ (o) => (0, import_react2.createElement)(
2717
+ RN.Pressable,
2718
+ {
2719
+ key: o.id,
2720
+ onPress: () => flows.onboarding.answer(o.id),
2721
+ accessibilityRole: "radio",
2722
+ accessibilityState: { checked: v.answer === o.id },
2723
+ style: {
2724
+ minHeight: 44,
2725
+ paddingHorizontal: 12,
2726
+ borderRadius: 10,
2727
+ borderWidth: 1,
2728
+ borderColor: v.answer === o.id ? p.accent : p.line,
2729
+ backgroundColor: v.answer === o.id ? p.soft : p.bg,
2730
+ justifyContent: "center"
2731
+ }
2732
+ },
2733
+ text2(o.label, { fontWeight: "500" })
2734
+ )
2735
+ )
2736
+ ) : q ? (0, import_react2.createElement)(RN.TextInput, {
2737
+ key: "answer",
2738
+ value: v.answer ?? "",
2739
+ onChangeText: (t) => flows.onboarding.answer(t),
2740
+ onSubmitEditing: flows.onboarding.next,
2741
+ placeholder: q.placeholder || "Your answer",
2742
+ placeholderTextColor: p.muted,
2743
+ maxLength: 500,
2744
+ accessibilityLabel: s.title,
2745
+ style: { height: 44, borderWidth: 1, borderColor: p.line, borderRadius: 10, paddingHorizontal: 12, color: p.fg }
2746
+ }) : null,
2747
+ btn(s.button || (v.last ? "Done" : "Continue"), "primary", flows.onboarding.next, !ok),
2748
+ v.back ? btn("Back", "quiet", flows.onboarding.back) : null
2749
+ ],
2750
+ flows.onboarding.dismiss
2751
+ );
2752
+ }
2168
2753
  if (v.type === "step")
2169
2754
  return sheet(
2170
2755
  [
2171
- (0, import_react.createElement)(
2756
+ (0, import_react2.createElement)(
2172
2757
  RN.View,
2173
2758
  { style: { flexDirection: "row", alignItems: "center" } },
2174
2759
  v.total > 1 ? muted(`Step ${v.index + 1} of ${v.total}`) : null,
2175
2760
  close("Close the tour", flows.onboarding.dismiss)
2176
2761
  ),
2762
+ v.step.kind === "modal" && v.flow.icon ? (0, import_react2.createElement)(RN.View, { key: "icon" }, iconNode(v.flow.icon, 36, p.accent)) : null,
2763
+ media(v.step),
2177
2764
  title(v.step.title),
2178
2765
  v.step.body ? muted(v.step.body, { fontSize: 15 }) : null,
2179
2766
  btn(v.step.button || (v.last ? "Done" : "Next"), "primary", flows.onboarding.next)
@@ -2182,11 +2769,11 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2182
2769
  );
2183
2770
  if (v.type === "checklist") {
2184
2771
  const done = v.items.filter((i) => i.done).length;
2185
- return (0, import_react.createElement)(
2772
+ return (0, import_react2.createElement)(
2186
2773
  RN.View,
2187
2774
  { pointerEvents: "box-none", style: { position: "absolute", left: 12, right: 12, bottom: 28 } },
2188
2775
  card([
2189
- (0, import_react.createElement)(
2776
+ (0, import_react2.createElement)(
2190
2777
  RN.View,
2191
2778
  { key: "t", style: { flexDirection: "row", alignItems: "center" } },
2192
2779
  title(v.flow.name),
@@ -2194,17 +2781,19 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2194
2781
  ),
2195
2782
  muted(`${done} of ${v.items.length} done`),
2196
2783
  ...v.items.map(
2197
- ({ step, done: d }) => (0, import_react.createElement)(
2784
+ ({ step, done: d, opens }) => (0, import_react2.createElement)(
2198
2785
  RN.Pressable,
2199
2786
  {
2200
2787
  key: step.id,
2201
- disabled: d || !!step.event,
2202
- onPress: () => flows.onboarding.check(step.id),
2203
- accessibilityRole: "checkbox",
2788
+ // 0.17.0/0.18.0: a task that opens something (a tour, a modal, a screen, the app's code) does it;
2789
+ // a finished tour or modal ticks it, a screen or the app's code once opened (unless an event does).
2790
+ disabled: d || !!step.event && !opens,
2791
+ onPress: () => opens ? flows.onboarding.open(step.id) : flows.onboarding.check(step.id),
2792
+ accessibilityRole: opens && !d ? "button" : "checkbox",
2204
2793
  accessibilityState: { checked: d },
2205
2794
  style: { flexDirection: "row", gap: 10, alignItems: "flex-start", paddingVertical: 6 }
2206
2795
  },
2207
- (0, import_react.createElement)(
2796
+ (0, import_react2.createElement)(
2208
2797
  RN.View,
2209
2798
  {
2210
2799
  style: {
@@ -2220,7 +2809,8 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2220
2809
  },
2221
2810
  d ? text2("\u2713", { color: "#fff", fontSize: 12 }) : null
2222
2811
  ),
2223
- (0, import_react.createElement)(
2812
+ step.icon ? iconNode(step.icon, 20, p.accent) : null,
2813
+ (0, import_react2.createElement)(
2224
2814
  RN.View,
2225
2815
  { style: { flex: 1 } },
2226
2816
  text2(step.title, { fontWeight: "600", color: d ? p.muted : p.fg, textDecorationLine: d ? "line-through" : "none" }),
@@ -2233,14 +2823,14 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2233
2823
  }
2234
2824
  const m = v.message;
2235
2825
  if (m.style === "banner")
2236
- return (0, import_react.createElement)(
2826
+ return (0, import_react2.createElement)(
2237
2827
  RN.View,
2238
2828
  { pointerEvents: "box-none", style: { position: "absolute", left: 12, right: 12, top: 56 } },
2239
2829
  card([
2240
- (0, import_react.createElement)(
2830
+ (0, import_react2.createElement)(
2241
2831
  RN.View,
2242
2832
  { key: "t", style: { flexDirection: "row", alignItems: "center", gap: 8 } },
2243
- (0, import_react.createElement)(RN.View, { style: { flex: 1 } }, text2(m.title, { fontWeight: "700" }), m.body ? muted(m.body) : null),
2833
+ (0, import_react2.createElement)(RN.View, { style: { flex: 1 } }, text2(m.title, { fontWeight: "700" }), m.body ? muted(m.body) : null),
2244
2834
  close("Dismiss", flows.message.dismiss)
2245
2835
  ),
2246
2836
  m.button ? btn(m.button, "primary", flows.message.click) : null
@@ -2248,7 +2838,7 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
2248
2838
  );
2249
2839
  return sheet(
2250
2840
  [
2251
- (0, import_react.createElement)(RN.View, { key: "t", style: { flexDirection: "row" } }, close("Dismiss", flows.message.dismiss)),
2841
+ (0, import_react2.createElement)(RN.View, { key: "t", style: { flexDirection: "row" } }, close("Dismiss", flows.message.dismiss)),
2252
2842
  title(m.title),
2253
2843
  m.body ? muted(m.body, { fontSize: 15 }) : null,
2254
2844
  m.button ? btn(m.button, "primary", flows.message.click) : null
@@ -2431,7 +3021,7 @@ var optionalModules = () => ({
2431
3021
  });
2432
3022
 
2433
3023
  // src/native/widgets-view.ts
2434
- var import_react2 = require("react");
3024
+ var import_react3 = require("react");
2435
3025
  var RN3 = require("react-native");
2436
3026
  var usePalette = (appearance = {}) => {
2437
3027
  const scheme = RN3.useColorScheme?.();
@@ -2439,10 +3029,10 @@ var usePalette = (appearance = {}) => {
2439
3029
  return { ...dark ? DARK : LIGHT, accent: appearance.accent ?? "#3f76ff", danger: "#d92d20" };
2440
3030
  };
2441
3031
  var kit = (p) => {
2442
- const text2 = (children, style = {}) => (0, import_react2.createElement)(RN3.Text, { style: { color: p.fg, fontSize: 15, ...style } }, children);
3032
+ const text2 = (children, style = {}) => (0, import_react3.createElement)(RN3.Text, { style: { color: p.fg, fontSize: 15, ...style } }, children);
2443
3033
  const muted = (children, style = {}) => text2(children, { color: p.muted, fontSize: 13, ...style });
2444
- const title = (children) => (0, import_react2.createElement)(RN3.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700", flex: 1 } }, children);
2445
- const btn = (label, kind, onPress, disabled = false, style = {}) => (0, import_react2.createElement)(
3034
+ const title = (children) => (0, import_react3.createElement)(RN3.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700", flex: 1 } }, children);
3035
+ const btn = (label, kind, onPress, disabled = false, style = {}) => (0, import_react3.createElement)(
2446
3036
  RN3.Pressable,
2447
3037
  {
2448
3038
  key: label,
@@ -2465,8 +3055,8 @@ var kit = (p) => {
2465
3055
  },
2466
3056
  text2(label, { color: kind === "primary" ? "#ffffff" : p.fg, fontWeight: "600" })
2467
3057
  );
2468
- const closeX = (label, onPress) => (0, import_react2.createElement)(RN3.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, hitSlop: 12, style: { padding: 4 } }, text2("\xD7", { fontSize: 24, color: p.muted }));
2469
- const field = (o) => (0, import_react2.createElement)(RN3.TextInput, {
3058
+ const closeX = (label, onPress) => (0, import_react3.createElement)(RN3.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, hitSlop: 12, style: { padding: 4 } }, text2("\xD7", { fontSize: 24, color: p.muted }));
3059
+ const field = (o) => (0, import_react3.createElement)(RN3.TextInput, {
2470
3060
  value: o.value,
2471
3061
  onChangeText: o.onChange,
2472
3062
  placeholder: o.placeholder,
@@ -2489,7 +3079,7 @@ var kit = (p) => {
2489
3079
  ...o.style
2490
3080
  }
2491
3081
  });
2492
- const chip = (label, on, onPress, key = label) => (0, import_react2.createElement)(
3082
+ const chip = (label, on, onPress, key = label) => (0, import_react3.createElement)(
2493
3083
  RN3.Pressable,
2494
3084
  {
2495
3085
  key,
@@ -2500,13 +3090,13 @@ var kit = (p) => {
2500
3090
  },
2501
3091
  text2(label, { fontSize: 13, fontWeight: "600", color: on ? p.accent : p.fg })
2502
3092
  );
2503
- const page = (heading, onClose, children) => (0, import_react2.createElement)(
3093
+ const page = (heading, onClose, children) => (0, import_react3.createElement)(
2504
3094
  RN3.Modal,
2505
3095
  { visible: true, animationType: "slide", presentationStyle: "pageSheet", onRequestClose: onClose },
2506
- (0, import_react2.createElement)(
3096
+ (0, import_react3.createElement)(
2507
3097
  RN3.KeyboardAvoidingView,
2508
3098
  { behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor: p.bg } },
2509
- (0, import_react2.createElement)(
3099
+ (0, import_react3.createElement)(
2510
3100
  RN3.View,
2511
3101
  { style: { flexDirection: "row", alignItems: "center", paddingHorizontal: 16, paddingTop: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: p.line } },
2512
3102
  title(heading),
@@ -2534,8 +3124,8 @@ var opener = () => {
2534
3124
  listeners.forEach((l) => l(v));
2535
3125
  };
2536
3126
  const useOpen = () => {
2537
- const [v, setV] = (0, import_react2.useState)(open);
2538
- (0, import_react2.useEffect)(() => {
3127
+ const [v, setV] = (0, import_react3.useState)(open);
3128
+ (0, import_react3.useEffect)(() => {
2539
3129
  listeners.add(setV);
2540
3130
  setV(open);
2541
3131
  return () => {
@@ -2549,19 +3139,19 @@ var opener = () => {
2549
3139
  var isAnswered = (a) => Array.isArray(a) ? a.length > 0 : typeof a === "number" ? true : !!a?.trim();
2550
3140
  var SurveySheet = ({ survey, api, p, onDone }) => {
2551
3141
  const k = kit(p);
2552
- const [step, setStep] = (0, import_react2.useState)(0);
2553
- const [answers, setAnswers] = (0, import_react2.useState)({});
2554
- const [done, setDone] = (0, import_react2.useState)(false);
2555
- const [error, setError] = (0, import_react2.useState)("");
2556
- const started = (0, import_react2.useRef)(Date.now());
2557
- const finished = (0, import_react2.useRef)(false);
3142
+ const [step, setStep] = (0, import_react3.useState)(0);
3143
+ const [answers, setAnswers] = (0, import_react3.useState)({});
3144
+ const [done, setDone] = (0, import_react3.useState)(false);
3145
+ const [error, setError] = (0, import_react3.useState)("");
3146
+ const started = (0, import_react3.useRef)(Date.now());
3147
+ const finished = (0, import_react3.useRef)(false);
2558
3148
  const q = survey.questions[step];
2559
3149
  const last = step === survey.questions.length - 1;
2560
3150
  const value = answers[q.key];
2561
- (0, import_react2.useEffect)(() => {
3151
+ (0, import_react3.useEffect)(() => {
2562
3152
  api.seen(survey.id).catch(() => void 0);
2563
3153
  }, [api, survey.id]);
2564
- (0, import_react2.useEffect)(() => {
3154
+ (0, import_react3.useEffect)(() => {
2565
3155
  if (!done) return;
2566
3156
  const t = setTimeout(onDone, 2500);
2567
3157
  return () => clearTimeout(t);
@@ -2590,16 +3180,16 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
2590
3180
  if (q.kind === "nps" || q.kind === "rating") {
2591
3181
  const scale = q.kind === "nps" ? 11 : Math.min(Math.max(q.scale ?? 5, 2), 10);
2592
3182
  const start = q.kind === "nps" ? 0 : 1;
2593
- input = (0, import_react2.createElement)(
3183
+ input = (0, import_react3.createElement)(
2594
3184
  RN3.View,
2595
3185
  { style: { gap: 6 } },
2596
- (0, import_react2.createElement)(
3186
+ (0, import_react3.createElement)(
2597
3187
  RN3.View,
2598
3188
  { style: { flexDirection: "row", flexWrap: "wrap", gap: 6 }, accessibilityRole: "radiogroup", accessibilityLabel: q.title },
2599
3189
  ...Array.from({ length: scale }, (_, i) => {
2600
3190
  const n = i + start;
2601
3191
  const on = value === n;
2602
- return (0, import_react2.createElement)(
3192
+ return (0, import_react3.createElement)(
2603
3193
  RN3.Pressable,
2604
3194
  {
2605
3195
  key: n,
@@ -2613,17 +3203,17 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
2613
3203
  );
2614
3204
  })
2615
3205
  ),
2616
- q.low || q.high || q.kind === "nps" ? (0, import_react2.createElement)(RN3.View, { style: { flexDirection: "row", justifyContent: "space-between" } }, k.muted(q.low ?? (q.kind === "nps" ? "Not likely" : "")), k.muted(q.high ?? (q.kind === "nps" ? "Very likely" : ""))) : null
3206
+ q.low || q.high || q.kind === "nps" ? (0, import_react3.createElement)(RN3.View, { style: { flexDirection: "row", justifyContent: "space-between" } }, k.muted(q.low ?? (q.kind === "nps" ? "Not likely" : "")), k.muted(q.high ?? (q.kind === "nps" ? "Very likely" : ""))) : null
2617
3207
  );
2618
3208
  } else if (q.kind === "single" || q.kind === "multi") {
2619
3209
  const multi = q.kind === "multi";
2620
3210
  const picked = multi ? value ?? [] : [];
2621
- input = (0, import_react2.createElement)(
3211
+ input = (0, import_react3.createElement)(
2622
3212
  RN3.View,
2623
3213
  { style: { gap: 6 } },
2624
3214
  ...(q.options ?? []).map((o) => {
2625
3215
  const on = multi ? picked.includes(o) : value === o;
2626
- return (0, import_react2.createElement)(
3216
+ return (0, import_react3.createElement)(
2627
3217
  RN3.Pressable,
2628
3218
  {
2629
3219
  key: o,
@@ -2638,26 +3228,26 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
2638
3228
  );
2639
3229
  } else input = k.field({ value: value ?? "", onChange: pick, placeholder: q.placeholder ?? "Type your answer", multiline: true });
2640
3230
  const body = done ? [k.text(survey.thanks || "Thanks for your feedback.", { fontWeight: "600", textAlign: "center", paddingVertical: 16 })] : [
2641
- (0, import_react2.createElement)(
3231
+ (0, import_react3.createElement)(
2642
3232
  RN3.View,
2643
3233
  { key: "top", style: { flexDirection: "row", alignItems: "center", gap: 4 } },
2644
- ...survey.questions.map((x, i) => (0, import_react2.createElement)(RN3.View, { key: x.key, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= step ? p.accent : p.line } })),
2645
- (0, import_react2.createElement)(RN3.View, { style: { flex: 1 } }),
3234
+ ...survey.questions.map((x, i) => (0, import_react3.createElement)(RN3.View, { key: x.key, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= step ? p.accent : p.line } })),
3235
+ (0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
2646
3236
  k.closeX("Close survey", dismiss)
2647
3237
  ),
2648
- (0, import_react2.createElement)(RN3.Text, { key: "q", accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700" } }, q.title, q.required ? "" : (0, import_react2.createElement)(RN3.Text, { style: { color: p.muted, fontSize: 13, fontWeight: "400" } }, " optional")),
3238
+ (0, import_react3.createElement)(RN3.Text, { key: "q", accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700" } }, q.title, q.required ? "" : (0, import_react3.createElement)(RN3.Text, { style: { color: p.muted, fontSize: 13, fontWeight: "400" } }, " optional")),
2649
3239
  q.description ? k.muted(q.description) : null,
2650
3240
  input,
2651
3241
  error ? k.text(error, { color: p.danger, fontSize: 13 }) : null,
2652
3242
  k.btn(last ? "Send" : "Next", "primary", next)
2653
3243
  ];
2654
- return (0, import_react2.createElement)(
3244
+ return (0, import_react3.createElement)(
2655
3245
  RN3.Modal,
2656
3246
  { visible: true, transparent: true, animationType: "slide", onRequestClose: dismiss },
2657
- (0, import_react2.createElement)(
3247
+ (0, import_react3.createElement)(
2658
3248
  RN3.KeyboardAvoidingView,
2659
3249
  { behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor: "rgba(0,0,0,0.35)", justifyContent: "flex-end" } },
2660
- (0, import_react2.createElement)(
3250
+ (0, import_react3.createElement)(
2661
3251
  RN3.View,
2662
3252
  {
2663
3253
  accessibilityViewIsModal: true,
@@ -2671,10 +3261,10 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
2671
3261
  };
2672
3262
  var MRRocketSurveys = ({ client, appearance }) => {
2673
3263
  const p = usePalette(appearance);
2674
- const [survey, setSurvey] = (0, import_react2.useState)(null);
2675
- const [who, setWho] = (0, import_react2.useState)(() => client.getDistinctId());
2676
- (0, import_react2.useEffect)(() => client.subscribe(() => setWho(client.getDistinctId())), [client]);
2677
- (0, import_react2.useEffect)(() => {
3264
+ const [survey, setSurvey] = (0, import_react3.useState)(null);
3265
+ const [who2, setWho] = (0, import_react3.useState)(() => client.getDistinctId());
3266
+ (0, import_react3.useEffect)(() => client.subscribe(() => setWho(client.getDistinctId())), [client]);
3267
+ (0, import_react3.useEffect)(() => {
2678
3268
  let stop = false;
2679
3269
  const ctrl = new AbortController();
2680
3270
  const timer = setTimeout(() => {
@@ -2693,34 +3283,34 @@ var MRRocketSurveys = ({ client, appearance }) => {
2693
3283
  clearTimeout(timer);
2694
3284
  ctrl.abort();
2695
3285
  };
2696
- }, [client, who]);
2697
- const done = (0, import_react2.useCallback)(() => {
3286
+ }, [client, who2]);
3287
+ const done = (0, import_react3.useCallback)(() => {
2698
3288
  setSurvey((cur) => {
2699
3289
  if (cur) client.markSurveyDone(cur.s.id);
2700
3290
  return null;
2701
3291
  });
2702
3292
  }, [client]);
2703
3293
  if (!survey) return null;
2704
- return (0, import_react2.createElement)(SurveySheet, { key: survey.s.id, survey: survey.s, api: survey.api, p, onDone: done });
3294
+ return (0, import_react3.createElement)(SurveySheet, { key: survey.s.id, survey: survey.s, api: survey.api, p, onDone: done });
2705
3295
  };
2706
3296
  var feedbackOpen = opener();
2707
3297
  var useFeedback = () => ({ open: () => feedbackOpen.set(true), close: () => feedbackOpen.set(false) });
2708
3298
  var FeedbackSheet = ({ client, p, title }) => {
2709
3299
  const k = kit(p);
2710
- const [tab, setTab] = (0, import_react2.useState)("top");
2711
- const [posts, setPosts] = (0, import_react2.useState)(null);
2712
- const [boards, setBoards] = (0, import_react2.useState)([]);
2713
- const [error, setError] = (0, import_react2.useState)("");
2714
- const [board, setBoard] = (0, import_react2.useState)("feature");
2715
- const [subject, setSubject] = (0, import_react2.useState)("");
2716
- const [details, setDetails] = (0, import_react2.useState)("");
2717
- const [name, setName] = (0, import_react2.useState)("");
2718
- const [email, setEmail] = (0, import_react2.useState)("");
2719
- const [sending, setSending] = (0, import_react2.useState)(false);
2720
- const [sent, setSent] = (0, import_react2.useState)(false);
3300
+ const [tab, setTab] = (0, import_react3.useState)("top");
3301
+ const [posts, setPosts] = (0, import_react3.useState)(null);
3302
+ const [boards, setBoards] = (0, import_react3.useState)([]);
3303
+ const [error, setError] = (0, import_react3.useState)("");
3304
+ const [board, setBoard] = (0, import_react3.useState)("feature");
3305
+ const [subject, setSubject] = (0, import_react3.useState)("");
3306
+ const [details, setDetails] = (0, import_react3.useState)("");
3307
+ const [name, setName] = (0, import_react3.useState)("");
3308
+ const [email, setEmail] = (0, import_react3.useState)("");
3309
+ const [sending, setSending] = (0, import_react3.useState)(false);
3310
+ const [sent, setSent] = (0, import_react3.useState)(false);
2721
3311
  const close = () => feedbackOpen.set(false);
2722
3312
  const user = client.getUser();
2723
- (0, import_react2.useEffect)(() => {
3313
+ (0, import_react3.useEffect)(() => {
2724
3314
  const api = client.api();
2725
3315
  if (!api) return setError("Feedback is off while tracking is off.");
2726
3316
  const ctrl = new AbortController();
@@ -2752,7 +3342,7 @@ var FeedbackSheet = ({ client, p, title }) => {
2752
3342
  setSent(true);
2753
3343
  }).catch((e) => setError(e.message)).finally(() => setSending(false));
2754
3344
  };
2755
- const tabs = (0, import_react2.createElement)(
3345
+ const tabs = (0, import_react3.createElement)(
2756
3346
  RN3.View,
2757
3347
  { style: { flexDirection: "row", gap: 8, paddingHorizontal: 16, paddingTop: 12 } },
2758
3348
  k.chip("Top requests", tab === "top", () => setTab("top")),
@@ -2761,17 +3351,17 @@ var FeedbackSheet = ({ client, p, title }) => {
2761
3351
  setSent(false);
2762
3352
  })
2763
3353
  );
2764
- const list = (0, import_react2.createElement)(
3354
+ const list = (0, import_react3.createElement)(
2765
3355
  RN3.ScrollView,
2766
3356
  { style: { flex: 1 }, contentContainerStyle: { padding: 16, gap: 10 } },
2767
3357
  error ? k.text(error, { color: p.danger, fontSize: 13 }) : null,
2768
3358
  posts === null && !error ? k.muted("Loading\u2026") : null,
2769
3359
  posts?.length === 0 ? k.muted("No requests yet. Be the first to suggest one.") : null,
2770
3360
  ...(posts ?? []).map(
2771
- (post) => (0, import_react2.createElement)(
3361
+ (post) => (0, import_react3.createElement)(
2772
3362
  RN3.View,
2773
3363
  { key: post.id, style: { flexDirection: "row", gap: 12, alignItems: "flex-start", paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: p.line } },
2774
- (0, import_react2.createElement)(
3364
+ (0, import_react3.createElement)(
2775
3365
  RN3.Pressable,
2776
3366
  {
2777
3367
  onPress: () => vote(post),
@@ -2783,15 +3373,15 @@ var FeedbackSheet = ({ client, p, title }) => {
2783
3373
  k.text("\u25B2", { fontSize: 11, color: post.voted ? p.accent : p.muted }),
2784
3374
  k.text(String(post.votes), { fontWeight: "700", fontSize: 14, color: post.voted ? p.accent : p.fg })
2785
3375
  ),
2786
- (0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 2 } }, k.text(post.title, { fontWeight: "600" }), post.body ? k.muted(post.body.slice(0, 160)) : null)
3376
+ (0, import_react3.createElement)(RN3.View, { style: { flex: 1, gap: 2 } }, k.text(post.title, { fontWeight: "600" }), post.body ? k.muted(post.body.slice(0, 160)) : null)
2787
3377
  )
2788
3378
  )
2789
3379
  );
2790
- const form = (0, import_react2.createElement)(
3380
+ const form = (0, import_react3.createElement)(
2791
3381
  RN3.ScrollView,
2792
3382
  { style: { flex: 1 }, contentContainerStyle: { padding: 16, gap: 10 }, keyboardShouldPersistTaps: "handled" },
2793
3383
  sent ? k.text("Thanks! Your request is on the board.", { fontWeight: "600" }) : null,
2794
- boards.length > 1 ? (0, import_react2.createElement)(RN3.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 6 } }, ...boards.map((b) => k.chip(b.label, board === b.id, () => setBoard(b.id), b.id))) : null,
3384
+ boards.length > 1 ? (0, import_react3.createElement)(RN3.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 6 } }, ...boards.map((b) => k.chip(b.label, board === b.id, () => setBoard(b.id), b.id))) : null,
2795
3385
  k.field({ value: subject, onChange: setSubject, placeholder: "Short title", max: 120 }),
2796
3386
  k.field({ value: details, onChange: setDetails, placeholder: "Details (optional)", multiline: true }),
2797
3387
  ...user?.email ? [] : [
@@ -2807,7 +3397,7 @@ var FeedbackSheet = ({ client, p, title }) => {
2807
3397
  var MRRocketFeedback = ({ client, appearance, options = {} }) => {
2808
3398
  const p = usePalette(appearance);
2809
3399
  const open = feedbackOpen.useOpen();
2810
- return open ? (0, import_react2.createElement)(FeedbackSheet, { client, p, title: options.title ?? "Feedback" }) : null;
3400
+ return open ? (0, import_react3.createElement)(FeedbackSheet, { client, p, title: options.title ?? "Feedback" }) : null;
2811
3401
  };
2812
3402
  var POLL_OPEN_MS = 4e3;
2813
3403
  var POLL_WAITING_MS = 8e3;
@@ -2815,6 +3405,7 @@ var POLL_IDLE_MS = 3e4;
2815
3405
  var WAITING_FOR_MS = 10 * 6e4;
2816
3406
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
2817
3407
  var whoLabel = { visitor: "You", team: "Team", ai: "AI agent", auto: "Team" };
3408
+ var who = (from, agentName) => from === "ai" && agentName?.trim() || whoLabel[from];
2818
3409
  var msgView = "home";
2819
3410
  var messengerOpen = opener();
2820
3411
  var openAt = (v = "home") => {
@@ -2846,11 +3437,11 @@ try {
2846
3437
  }
2847
3438
  var PHOTO_TYPES = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif" };
2848
3439
  var teamOf = (t) => (t?.messages ?? []).filter((m) => m.from !== "visitor");
2849
- var svg = null;
3440
+ var svg2 = null;
2850
3441
  try {
2851
- svg = require("react-native-svg");
3442
+ svg2 = require("react-native-svg");
2852
3443
  } catch {
2853
- svg = null;
3444
+ svg2 = null;
2854
3445
  }
2855
3446
  var GLYPH = {
2856
3447
  chat: "M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z",
@@ -2865,15 +3456,15 @@ var GLYPH = {
2865
3456
  };
2866
3457
  var FALLBACK = { chat: "\u{1F4AC}", home: "\u2302", help: "?", send: "\u2192", up: "\u2191", next: "\u203A", search: "\u2315", cross: "\xD7", clip: "\u{1F4CE}" };
2867
3458
  var glyph = (name, color, size = 20) => {
2868
- const Svg = svg?.Svg ?? svg?.default;
2869
- if (!svg || !Svg) return (0, import_react2.createElement)(RN3.Text, { style: { color, fontSize: Math.round(size * 0.85), lineHeight: size + 2, textAlign: "center", minWidth: size } }, FALLBACK[name]);
2870
- return (0, import_react2.createElement)(Svg, { width: size, height: size, viewBox: "0 0 16 16", fill: "none" }, (0, import_react2.createElement)(svg.Path, { d: GLYPH[name], stroke: color, strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }));
3459
+ const Svg = svg2?.Svg ?? svg2?.default;
3460
+ if (!svg2 || !Svg) return (0, import_react3.createElement)(RN3.Text, { style: { color, fontSize: Math.round(size * 0.85), lineHeight: size + 2, textAlign: "center", minWidth: size } }, FALLBACK[name]);
3461
+ return (0, import_react3.createElement)(Svg, { width: size, height: size, viewBox: "0 0 16 16", fill: "none" }, (0, import_react3.createElement)(svg2.Path, { d: GLYPH[name], stroke: color, strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }));
2871
3462
  };
2872
3463
  var alpha = (hex, a) => /^#[0-9a-f]{6}$/i.test(hex) ? `${hex}${Math.round(a * 255).toString(16).padStart(2, "0")}` : hex;
2873
- var teamMark = (title, bg, fg, size = 36) => (0, import_react2.createElement)(
3464
+ var teamMark = (title, bg, fg, size = 36) => (0, import_react3.createElement)(
2874
3465
  RN3.View,
2875
3466
  { style: { width: size, height: size, borderRadius: size / 2, backgroundColor: bg, alignItems: "center", justifyContent: "center" } },
2876
- (0, import_react2.createElement)(RN3.Text, { style: { color: fg, fontSize: Math.round(size * 0.42), fontWeight: "700" } }, (title.trim()[0] ?? "?").toUpperCase())
3467
+ (0, import_react3.createElement)(RN3.Text, { style: { color: fg, fontSize: Math.round(size * 0.42), fontWeight: "700" } }, (title.trim()[0] ?? "?").toUpperCase())
2877
3468
  );
2878
3469
  var HU = {
2879
3470
  light: { bg: "#ffffff", surface: "#f4f4f5", def: "#ececee", segment: "#ffffff", fg: "#18181b", muted: "#71717a" },
@@ -2881,20 +3472,20 @@ var HU = {
2881
3472
  };
2882
3473
  var hairline = { shadowColor: "#000", shadowOpacity: 0.07, shadowRadius: 3, shadowOffset: { width: 0, height: 1 }, elevation: 1 };
2883
3474
  var useChat = (client, o, open, view) => {
2884
- const [thread, setThread] = (0, import_react2.useState)(null);
2885
- const [loaded, setLoaded] = (0, import_react2.useState)(false);
2886
- const [error, setError] = (0, import_react2.useState)("");
2887
- const [unseen, setUnseen] = (0, import_react2.useState)([]);
2888
- const known = (0, import_react2.useRef)(null);
3475
+ const [thread, setThread] = (0, import_react3.useState)(null);
3476
+ const [loaded, setLoaded] = (0, import_react3.useState)(false);
3477
+ const [error, setError] = (0, import_react3.useState)("");
3478
+ const [unseen, setUnseen] = (0, import_react3.useState)([]);
3479
+ const known = (0, import_react3.useRef)(null);
2889
3480
  const watching = open && view === "messages";
2890
- const watch = (0, import_react2.useRef)(watching);
3481
+ const watch = (0, import_react3.useRef)(watching);
2891
3482
  watch.current = watching;
2892
- const [active, setActive] = (0, import_react2.useState)(RN3.AppState.currentState === "active");
2893
- (0, import_react2.useEffect)(() => {
3483
+ const [active, setActive] = (0, import_react3.useState)(RN3.AppState.currentState === "active");
3484
+ (0, import_react3.useEffect)(() => {
2894
3485
  const sub = RN3.AppState.addEventListener("change", (st) => setActive(st === "active"));
2895
3486
  return () => sub.remove();
2896
3487
  }, []);
2897
- const take = (0, import_react2.useCallback)(
3488
+ const take = (0, import_react3.useCallback)(
2898
3489
  (t) => {
2899
3490
  const team = teamOf(t);
2900
3491
  if (known.current !== null) {
@@ -2910,10 +3501,10 @@ var useChat = (client, o, open, view) => {
2910
3501
  },
2911
3502
  [o.haptics]
2912
3503
  );
2913
- (0, import_react2.useEffect)(() => {
3504
+ (0, import_react3.useEffect)(() => {
2914
3505
  if (watching) setUnseen([]);
2915
3506
  }, [watching]);
2916
- (0, import_react2.useEffect)(() => {
3507
+ (0, import_react3.useEffect)(() => {
2917
3508
  if (!active) return;
2918
3509
  let stop = false;
2919
3510
  let timer;
@@ -2953,9 +3544,9 @@ var useChat = (client, o, open, view) => {
2953
3544
  return { thread, loaded, error, setError, unseen, take };
2954
3545
  };
2955
3546
  var useArticles = (client, on, query) => {
2956
- const [list, setList] = (0, import_react2.useState)(null);
2957
- const [any, setAny] = (0, import_react2.useState)(null);
2958
- (0, import_react2.useEffect)(() => {
3547
+ const [list, setList] = (0, import_react3.useState)(null);
3548
+ const [any, setAny] = (0, import_react3.useState)(null);
3549
+ (0, import_react3.useEffect)(() => {
2959
3550
  if (!on) return;
2960
3551
  let stop = false;
2961
3552
  const t = setTimeout(
@@ -2976,7 +3567,7 @@ var useArticles = (client, on, query) => {
2976
3567
  return { list, any };
2977
3568
  };
2978
3569
  var spansOf = (p, spans, base) => spans.map(
2979
- (s, i) => (0, import_react2.createElement)(
3570
+ (s, i) => (0, import_react3.createElement)(
2980
3571
  RN3.Text,
2981
3572
  {
2982
3573
  key: i,
@@ -2995,10 +3586,10 @@ var spansOf = (p, spans, base) => spans.map(
2995
3586
  );
2996
3587
  var ArticleView = ({ client, p, id, onAsk }) => {
2997
3588
  const k = kit(p);
2998
- const [a, setA] = (0, import_react2.useState)(null);
2999
- const [error, setError] = (0, import_react2.useState)("");
3000
- const [rated, setRated] = (0, import_react2.useState)(false);
3001
- (0, import_react2.useEffect)(() => {
3589
+ const [a, setA] = (0, import_react3.useState)(null);
3590
+ const [error, setError] = (0, import_react3.useState)("");
3591
+ const [rated, setRated] = (0, import_react3.useState)(false);
3592
+ (0, import_react3.useEffect)(() => {
3002
3593
  const ctrl = new AbortController();
3003
3594
  client.api()?.article(id, ctrl.signal).then(setA).catch((e) => e.name !== "AbortError" && setError(e.message));
3004
3595
  return () => ctrl.abort();
@@ -3007,30 +3598,30 @@ var ArticleView = ({ client, p, id, onAsk }) => {
3007
3598
  if (!a) return k.muted("Loading\u2026");
3008
3599
  const base = { color: p.fg, fontSize: 15, lineHeight: 22 };
3009
3600
  const blocks = a.blocks.map((b, i) => {
3010
- if (b.type === "h2" || b.type === "h3") return (0, import_react2.createElement)(RN3.Text, { key: i, accessibilityRole: "header", style: { ...base, fontSize: b.type === "h2" ? 17 : 16, fontWeight: "700", marginTop: 8 } }, ...spansOf(p, b.spans, {}));
3011
- if (b.type === "p") return (0, import_react2.createElement)(RN3.Text, { key: i, style: base }, ...spansOf(p, b.spans, {}));
3012
- if (b.type === "quote") return (0, import_react2.createElement)(RN3.View, { key: i, style: { borderLeftWidth: 3, borderLeftColor: p.line, paddingLeft: 10 } }, (0, import_react2.createElement)(RN3.Text, { style: { ...base, color: p.muted } }, ...spansOf(p, b.spans, {})));
3601
+ if (b.type === "h2" || b.type === "h3") return (0, import_react3.createElement)(RN3.Text, { key: i, accessibilityRole: "header", style: { ...base, fontSize: b.type === "h2" ? 17 : 16, fontWeight: "700", marginTop: 8 } }, ...spansOf(p, b.spans, {}));
3602
+ if (b.type === "p") return (0, import_react3.createElement)(RN3.Text, { key: i, style: base }, ...spansOf(p, b.spans, {}));
3603
+ if (b.type === "quote") return (0, import_react3.createElement)(RN3.View, { key: i, style: { borderLeftWidth: 3, borderLeftColor: p.line, paddingLeft: 10 } }, (0, import_react3.createElement)(RN3.Text, { style: { ...base, color: p.muted } }, ...spansOf(p, b.spans, {})));
3013
3604
  if (b.type === "ul" || b.type === "ol")
3014
- return (0, import_react2.createElement)(
3605
+ return (0, import_react3.createElement)(
3015
3606
  RN3.View,
3016
3607
  { key: i, style: { gap: 4 } },
3017
- ...b.items.map((it, j) => (0, import_react2.createElement)(RN3.View, { key: j, style: { flexDirection: "row", gap: 8 } }, k.text(b.type === "ol" ? `${j + 1}.` : "\u2022", base), (0, import_react2.createElement)(RN3.Text, { style: { ...base, flex: 1 } }, ...spansOf(p, it, {}))))
3608
+ ...b.items.map((it, j) => (0, import_react3.createElement)(RN3.View, { key: j, style: { flexDirection: "row", gap: 8 } }, k.text(b.type === "ol" ? `${j + 1}.` : "\u2022", base), (0, import_react3.createElement)(RN3.Text, { style: { ...base, flex: 1 } }, ...spansOf(p, it, {}))))
3018
3609
  );
3019
- if (b.type === "code") return (0, import_react2.createElement)(RN3.View, { key: i, style: { backgroundColor: p.soft, borderRadius: 8, padding: 10 } }, k.text(b.text, { fontFamily: RN3.Platform.OS === "ios" ? "Menlo" : "monospace", fontSize: 13 }));
3020
- return (0, import_react2.createElement)(RN3.View, { key: i, style: { height: 1, backgroundColor: p.line } });
3610
+ if (b.type === "code") return (0, import_react3.createElement)(RN3.View, { key: i, style: { backgroundColor: p.soft, borderRadius: 8, padding: 10 } }, k.text(b.text, { fontFamily: RN3.Platform.OS === "ios" ? "Menlo" : "monospace", fontSize: 13 }));
3611
+ return (0, import_react3.createElement)(RN3.View, { key: i, style: { height: 1, backgroundColor: p.line } });
3021
3612
  });
3022
3613
  const rate = (helpful) => {
3023
3614
  setRated(true);
3024
3615
  client.api()?.rateArticle(id, helpful).catch(() => void 0);
3025
3616
  if (!helpful) onAsk();
3026
3617
  };
3027
- return (0, import_react2.createElement)(
3618
+ return (0, import_react3.createElement)(
3028
3619
  RN3.View,
3029
3620
  { style: { gap: 10 } },
3030
3621
  k.muted(a.collection),
3031
- (0, import_react2.createElement)(RN3.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 20, fontWeight: "700" } }, a.title),
3622
+ (0, import_react3.createElement)(RN3.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 20, fontWeight: "700" } }, a.title),
3032
3623
  ...blocks,
3033
- (0, import_react2.createElement)(
3624
+ (0, import_react3.createElement)(
3034
3625
  RN3.View,
3035
3626
  { style: { flexDirection: "row", alignItems: "center", gap: 8, padding: 12, borderRadius: 10, backgroundColor: p.soft, marginTop: 8 } },
3036
3627
  rated ? k.muted("Thanks for letting us know.") : k.muted("Did this answer your question?", { flex: 1 }),
@@ -3050,19 +3641,26 @@ var MessengerSheet = ({
3050
3641
  brand
3051
3642
  }) => {
3052
3643
  const k = kit(p);
3053
- const [body, setBody] = (0, import_react2.useState)("");
3054
- const [email, setEmail] = (0, import_react2.useState)("");
3055
- const [pending, setPending] = (0, import_react2.useState)(null);
3056
- const [photos, setPhotos] = (0, import_react2.useState)([]);
3057
- const [query, setQuery] = (0, import_react2.useState)("");
3644
+ const [body, setBody] = (0, import_react3.useState)("");
3645
+ const [email, setEmail] = (0, import_react3.useState)("");
3646
+ const [pending, setPending] = (0, import_react3.useState)(null);
3647
+ const [slow, setSlow] = (0, import_react3.useState)(false);
3648
+ (0, import_react3.useEffect)(() => {
3649
+ setSlow(false);
3650
+ if (!pending) return;
3651
+ const t = setTimeout(() => setSlow(true), 1500);
3652
+ return () => clearTimeout(t);
3653
+ }, [pending]);
3654
+ const [photos, setPhotos] = (0, import_react3.useState)([]);
3655
+ const [query, setQuery] = (0, import_react3.useState)("");
3058
3656
  const helpOn = o.help !== false;
3059
3657
  const arts = useArticles(client, helpOn, query);
3060
- const scroll = (0, import_react2.useRef)(null);
3658
+ const scroll = (0, import_react3.useRef)(null);
3061
3659
  const close = () => messengerOpen.set(false);
3062
3660
  const greeting = o.greeting ?? "Hi! How can we help?";
3063
3661
  const { thread } = chat;
3064
3662
  const count = (thread?.messages.length ?? 0) + (pending ? 1 : 0);
3065
- (0, import_react2.useEffect)(() => {
3663
+ (0, import_react3.useEffect)(() => {
3066
3664
  if (view === "messages") setTimeout(() => scroll.current?.scrollToEnd?.({ animated: true }), 50);
3067
3665
  }, [count, view]);
3068
3666
  const needEmail = (o.askEmail ?? true) && !thread?.hasEmail && !client.getUser()?.email;
@@ -3145,7 +3743,7 @@ var MessengerSheet = ({
3145
3743
  setPending(null);
3146
3744
  }
3147
3745
  };
3148
- const localUris = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3746
+ const localUris = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
3149
3747
  for (const f of photos) localUris.current.set(f.key, f.uri);
3150
3748
  const fileUrl = (id) => localUris.current.get(id) ?? client.api()?.attachmentUrl(id) ?? "";
3151
3749
  const ink = inkOn(p.accent);
@@ -3160,11 +3758,11 @@ var MessengerSheet = ({
3160
3758
  const title = o.title?.trim() ?? "";
3161
3759
  const bubble = (m, isPending = false) => {
3162
3760
  const mine = m.from === "visitor";
3163
- return (0, import_react2.createElement)(
3761
+ return (0, import_react3.createElement)(
3164
3762
  RN3.View,
3165
3763
  { key: m.id, style: { alignSelf: mine ? "flex-end" : "flex-start", maxWidth: "82%", gap: 4 } },
3166
3764
  ...(m.attachments ?? []).map(
3167
- (f) => (0, import_react2.createElement)(
3765
+ (f) => (0, import_react3.createElement)(
3168
3766
  RN3.Pressable,
3169
3767
  {
3170
3768
  key: f.id,
@@ -3173,10 +3771,10 @@ var MessengerSheet = ({
3173
3771
  accessibilityLabel: f.name,
3174
3772
  style: { alignSelf: mine ? "flex-end" : "flex-start", opacity: isPending ? 0.7 : 1 }
3175
3773
  },
3176
- f.type.startsWith("image/") ? (0, import_react2.createElement)(RN3.Image, { source: { uri: fileUrl(f.id) }, resizeMode: "cover", style: { width: 200, height: 150, borderRadius: r.bubble, backgroundColor: p.soft } }) : (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: p.soft, borderRadius: r.card, paddingHorizontal: 10, paddingVertical: 7 } }, k.text(`\u{1F4C4} ${f.name}`, { fontSize: 13 }))
3774
+ f.type.startsWith("image/") ? (0, import_react3.createElement)(RN3.Image, { source: { uri: fileUrl(f.id) }, resizeMode: "cover", style: { width: 200, height: 150, borderRadius: r.bubble, backgroundColor: p.soft } }) : (0, import_react3.createElement)(RN3.View, { style: { backgroundColor: p.soft, borderRadius: r.card, paddingHorizontal: 10, paddingVertical: 7 } }, k.text(`\u{1F4C4} ${f.name}`, { fontSize: 13 }))
3177
3775
  )
3178
3776
  ),
3179
- !m.body && m.attachments?.length ? null : (0, import_react2.createElement)(
3777
+ !m.body && m.attachments?.length ? null : (0, import_react3.createElement)(
3180
3778
  RN3.View,
3181
3779
  {
3182
3780
  style: {
@@ -3193,23 +3791,23 @@ var MessengerSheet = ({
3193
3791
  k.text(m.body, { color: mine ? ink : hu.fg })
3194
3792
  ),
3195
3793
  ...(m.articles ?? []).map(
3196
- (a) => (0, import_react2.createElement)(
3794
+ (a) => (0, import_react3.createElement)(
3197
3795
  RN3.Pressable,
3198
3796
  { key: a.id, onPress: () => setView({ article: a.id }), accessibilityRole: "link", style: { backgroundColor: p.soft, borderRadius: r.card, paddingHorizontal: 10, paddingVertical: 7 } },
3199
3797
  k.text(`\u{1F4C4} ${a.title}`, { fontSize: 13 })
3200
3798
  )
3201
3799
  ),
3202
- m.at ? k.muted(`${mine ? "" : `${whoLabel[m.from]} \xB7 `}${isPending ? "Sending\u2026" : timeAgo(m.at)}`, { fontSize: 11, textAlign: mine ? "right" : "left", paddingHorizontal: 6 }) : null
3800
+ m.at ? k.muted(`${mine ? "" : `${who(m.from, o.agentName)} \xB7 `}${isPending && slow ? "Sending\u2026" : timeAgo(m.at)}`, { fontSize: 11, textAlign: mine ? "right" : "left", paddingHorizontal: 6 }) : null
3203
3801
  );
3204
3802
  };
3205
- const row = (a) => (0, import_react2.createElement)(
3803
+ const row = (a) => (0, import_react3.createElement)(
3206
3804
  RN3.Pressable,
3207
3805
  { key: a.id, onPress: () => setView({ article: a.id }), accessibilityRole: "button", style: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 11, paddingHorizontal: 8 } },
3208
3806
  k.text(a.title, { flex: 1 }),
3209
3807
  glyph("next", p.accent, 14)
3210
3808
  );
3211
- const card = (children, style = {}) => (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: raised, borderRadius: r.card, padding: 16, ...surf, ...style } }, ...children);
3212
- const searchRow = (onPress) => (0, import_react2.createElement)(
3809
+ const card = (children, style = {}) => (0, import_react3.createElement)(RN3.View, { style: { backgroundColor: raised, borderRadius: r.card, padding: 16, ...surf, ...style } }, ...children);
3810
+ const searchRow = (onPress) => (0, import_react3.createElement)(
3213
3811
  RN3.Pressable,
3214
3812
  { key: "s", onPress, accessibilityRole: "search", style: { flexDirection: "row", alignItems: "center", height: 44, borderRadius: r.field, paddingHorizontal: 14, backgroundColor: hu.def } },
3215
3813
  k.muted("Search for help", { flex: 1, fontSize: 15 }),
@@ -3218,12 +3816,12 @@ var MessengerSheet = ({
3218
3816
  const search = k.field({ value: query, onChange: setQuery, placeholder: "Search for help", max: 200, style: { borderWidth: 0, borderRadius: r.field, backgroundColor: raised, ...surf } });
3219
3817
  const first = client.getUser()?.name?.trim().split(/\s+/)[0];
3220
3818
  const rest = greeting.replace(/^(hi|hello|hey|hallo)( there)?\s*[!,.]*\s*/i, "").trim() || "How can we help you today?";
3221
- const hero = (fg, sub) => (0, import_react2.createElement)(
3819
+ const hero = (fg, sub) => (0, import_react3.createElement)(
3222
3820
  RN3.Text,
3223
3821
  { key: "hero", accessibilityRole: "header", style: { color: fg, fontSize: 25, fontWeight: "600", letterSpacing: -0.6, lineHeight: 30 } },
3224
3822
  first ? `Hi, ${first} \u{1F44B}
3225
3823
  ` : "Hi there \u{1F44B}\n",
3226
- (0, import_react2.createElement)(RN3.Text, { style: { color: sub, fontWeight: "500" } }, rest)
3824
+ (0, import_react3.createElement)(RN3.Text, { style: { color: sub, fontWeight: "500" } }, rest)
3227
3825
  );
3228
3826
  const last = thread?.messages.at(-1);
3229
3827
  const showHelp = helpOn && arts.any !== false;
@@ -3231,11 +3829,11 @@ var MessengerSheet = ({
3231
3829
  if (view === "home") {
3232
3830
  const cards = [
3233
3831
  // "Ask a question": the words go into the chat's field and are sent as the chat opens.
3234
- (0, import_react2.createElement)(
3832
+ (0, import_react3.createElement)(
3235
3833
  RN3.View,
3236
3834
  { key: "ask", style: { flexDirection: "row", alignItems: "center", gap: 8, height: 48, borderRadius: 24, paddingLeft: 18, paddingRight: 6, backgroundColor: hu.surface, ...surf } },
3237
3835
  k.field({ value: body, onChange: setBody, placeholder: "Ask a question", max: 4e3, style: { flex: 1, borderWidth: 0, paddingHorizontal: 0, backgroundColor: "transparent" } }),
3238
- (0, import_react2.createElement)(
3836
+ (0, import_react3.createElement)(
3239
3837
  RN3.Pressable,
3240
3838
  {
3241
3839
  onPress: () => {
@@ -3249,60 +3847,60 @@ var MessengerSheet = ({
3249
3847
  glyph("up", body.trim() ? ink : hu.muted, 18)
3250
3848
  )
3251
3849
  ),
3252
- last ? (0, import_react2.createElement)(
3850
+ last ? (0, import_react3.createElement)(
3253
3851
  RN3.Pressable,
3254
3852
  { key: "recent", onPress: () => setView("messages"), accessibilityRole: "button" },
3255
3853
  card([
3256
- (0, import_react2.createElement)(
3854
+ (0, import_react3.createElement)(
3257
3855
  RN3.View,
3258
3856
  { key: "r", style: { flexDirection: "row", alignItems: "center", gap: 10 } },
3259
3857
  title ? teamMark(title, alpha(p.accent, 0.16), p.accent, 26) : null,
3260
- (0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, last.body || (last.attachments?.length ? `Sent ${last.attachments.length === 1 ? last.attachments[0].name : `${last.attachments.length} files`}` : "")), k.muted(`${last.from === "visitor" ? "You" : whoLabel[last.from]} \xB7 ${timeAgo(last.at)}`, { fontSize: 11.5 })),
3261
- chat.unseen.length ? (0, import_react2.createElement)(RN3.View, { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : glyph("next", p.muted, 14)
3858
+ (0, import_react3.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react3.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, last.body || (last.attachments?.length ? `Sent ${last.attachments.length === 1 ? last.attachments[0].name : `${last.attachments.length} files`}` : "")), k.muted(`${last.from === "visitor" ? "You" : who(last.from, o.agentName)} \xB7 ${timeAgo(last.at)}`, { fontSize: 11.5 })),
3859
+ chat.unseen.length ? (0, import_react3.createElement)(RN3.View, { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : glyph("next", p.muted, 14)
3262
3860
  )
3263
3861
  ], { paddingVertical: 10, paddingHorizontal: 12 })
3264
3862
  ) : null,
3265
3863
  showHelp ? card([searchRow(() => setView("help")), ...(arts.list ?? []).slice(0, 4).map(row)], { padding: 8, gap: 2 }) : null
3266
3864
  ];
3267
- content = plain ? (0, import_react2.createElement)(
3865
+ content = plain ? (0, import_react3.createElement)(
3268
3866
  RN3.ScrollView,
3269
3867
  { key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 24, gap: 12 }, keyboardShouldPersistTaps: "handled" },
3270
- (0, import_react2.createElement)(RN3.View, { key: "top", style: { paddingHorizontal: 6, paddingBottom: 10 } }, hero(p.fg, p.muted)),
3868
+ (0, import_react3.createElement)(RN3.View, { key: "top", style: { paddingHorizontal: 6, paddingBottom: 10 } }, hero(p.fg, p.muted)),
3271
3869
  ...cards
3272
- ) : (0, import_react2.createElement)(
3870
+ ) : (0, import_react3.createElement)(
3273
3871
  RN3.ScrollView,
3274
3872
  { key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingBottom: 24 }, keyboardShouldPersistTaps: "handled" },
3275
- (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: p.accent, paddingHorizontal: 22, paddingTop: 4, paddingBottom: 64 } }, hero(ink, alpha(ink, 0.8))),
3276
- (0, import_react2.createElement)(RN3.View, { style: { marginTop: -48, paddingHorizontal: 16, gap: 12 } }, ...cards)
3873
+ (0, import_react3.createElement)(RN3.View, { style: { backgroundColor: p.accent, paddingHorizontal: 22, paddingTop: 4, paddingBottom: 64 } }, hero(ink, alpha(ink, 0.8))),
3874
+ (0, import_react3.createElement)(RN3.View, { style: { marginTop: -48, paddingHorizontal: 16, gap: 12 } }, ...cards)
3277
3875
  );
3278
3876
  } else if (view === "messages")
3279
- content = (0, import_react2.createElement)(
3877
+ content = (0, import_react3.createElement)(
3280
3878
  RN3.View,
3281
3879
  { key: "messages", style: { flex: 1, backgroundColor: hu.bg } },
3282
- (0, import_react2.createElement)(
3880
+ (0, import_react3.createElement)(
3283
3881
  RN3.ScrollView,
3284
3882
  { ref: scroll, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 } },
3285
- bubble({ id: "greeting", from: "team", body: greeting, at: "" }),
3883
+ // The greeting opens an empty chat only, never above the visitor's first words (2026-10-30).
3884
+ !thread?.messages.length && !pending ? bubble({ id: "greeting", from: "team", body: greeting, at: "" }) : null,
3286
3885
  !chat.loaded ? k.muted("Loading\u2026") : null,
3287
3886
  ...(thread?.messages ?? []).map((m) => bubble(m)),
3288
3887
  pending ? bubble(pending, true) : null,
3289
3888
  thread?.conversation?.status === "solved" && !pending ? k.muted("This conversation is closed. Write to start a new one.", { textAlign: "center" }) : null
3290
3889
  ),
3291
- (0, import_react2.createElement)(
3890
+ (0, import_react3.createElement)(
3292
3891
  RN3.View,
3293
3892
  { style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom: 6, gap: 8 } },
3294
3893
  chat.error ? k.text(chat.error, { color: p.danger, fontSize: 12 }) : null,
3295
- needEmail ? k.field({ value: email, onChange: setEmail, placeholder: "Your email (optional), so we can follow up", email: true, max: 200, style: { borderWidth: emailOk ? 0 : 1, borderColor: p.danger, borderRadius: r.field, paddingHorizontal: 16, backgroundColor: hu.surface, ...surf } }) : null,
3296
3894
  // Photos picked for the next message, each with a way to take it out again.
3297
- photos.length ? (0, import_react2.createElement)(
3895
+ photos.length ? (0, import_react3.createElement)(
3298
3896
  RN3.View,
3299
3897
  { style: { flexDirection: "row", flexWrap: "wrap", gap: 10, paddingTop: 6, paddingHorizontal: 2 } },
3300
3898
  ...photos.map(
3301
- (f) => (0, import_react2.createElement)(
3899
+ (f) => (0, import_react3.createElement)(
3302
3900
  RN3.View,
3303
3901
  { key: f.key, style: { width: 56, height: 56 } },
3304
- (0, import_react2.createElement)(RN3.Image, { source: { uri: f.uri }, resizeMode: "cover", accessibilityLabel: f.id ? f.name : `${f.name}, uploading`, style: { width: 56, height: 56, borderRadius: Math.max(4, r.card - 4), backgroundColor: p.soft, opacity: f.id ? 1 : 0.5 } }),
3305
- (0, import_react2.createElement)(
3902
+ (0, import_react3.createElement)(RN3.Image, { source: { uri: f.uri }, resizeMode: "cover", accessibilityLabel: f.id ? f.name : `${f.name}, uploading`, style: { width: 56, height: 56, borderRadius: Math.max(4, r.card - 4), backgroundColor: p.soft, opacity: f.id ? 1 : 0.5 } }),
3903
+ (0, import_react3.createElement)(
3306
3904
  RN3.Pressable,
3307
3905
  {
3308
3906
  onPress: () => setPhotos((list) => list.filter((x) => x.key !== f.key)),
@@ -3317,20 +3915,29 @@ var MessengerSheet = ({
3317
3915
  )
3318
3916
  ) : null,
3319
3917
  // The message field on top, the paperclip (when the app has expo-image-picker) and the send button under it.
3320
- (0, import_react2.createElement)(
3918
+ (0, import_react3.createElement)(
3321
3919
  RN3.View,
3322
3920
  { style: { gap: 2, borderRadius: 20, paddingTop: 10, paddingBottom: 6, paddingRight: 6, paddingLeft: 16, backgroundColor: hu.surface, ...surf } },
3921
+ // The optional email: the box's first line, under a hairline the message follows.
3922
+ needEmail ? k.field({
3923
+ value: email,
3924
+ onChange: setEmail,
3925
+ placeholder: "Email for our reply (optional)",
3926
+ email: true,
3927
+ max: 200,
3928
+ style: { minHeight: 0, marginRight: 10, marginBottom: 4, paddingHorizontal: 0, paddingTop: 0, paddingBottom: 8, borderWidth: 0, borderRadius: 0, borderBottomWidth: 1, borderBottomColor: emailOk ? p.line : p.danger, backgroundColor: "transparent", fontSize: 13.5 }
3929
+ }) : null,
3323
3930
  k.field({ value: body, onChange: setBody, placeholder: "Write a message\u2026", multiline: true, max: 4e3, style: { minHeight: 28, maxHeight: 120, borderWidth: 0, paddingHorizontal: 0, paddingVertical: 4, backgroundColor: "transparent" } }),
3324
- (0, import_react2.createElement)(
3931
+ (0, import_react3.createElement)(
3325
3932
  RN3.View,
3326
3933
  { style: { flexDirection: "row", alignItems: "center", marginLeft: -8 } },
3327
- imagePicker?.launchImageLibraryAsync ? (0, import_react2.createElement)(
3934
+ imagePicker?.launchImageLibraryAsync ? (0, import_react3.createElement)(
3328
3935
  RN3.Pressable,
3329
3936
  { onPress: () => void pickPhotos(), disabled: !!pending, accessibilityRole: "button", accessibilityLabel: "Attach a photo or screenshot", hitSlop: 6, style: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center" } },
3330
3937
  glyph("clip", hu.muted, 19)
3331
3938
  ) : null,
3332
- (0, import_react2.createElement)(RN3.View, { style: { flex: 1 } }),
3333
- (0, import_react2.createElement)(
3939
+ (0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
3940
+ (0, import_react3.createElement)(
3334
3941
  RN3.Pressable,
3335
3942
  {
3336
3943
  onPress: () => void send(),
@@ -3348,76 +3955,78 @@ var MessengerSheet = ({
3348
3955
  else if (view === "help") {
3349
3956
  const groups = /* @__PURE__ */ new Map();
3350
3957
  for (const a of arts.list ?? []) groups.set(a.collection, [...groups.get(a.collection) ?? [], a]);
3351
- content = (0, import_react2.createElement)(
3958
+ content = (0, import_react3.createElement)(
3352
3959
  RN3.ScrollView,
3353
3960
  { key: "help", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 }, keyboardShouldPersistTaps: "handled" },
3354
3961
  search,
3355
3962
  arts.list === null ? k.muted("Loading\u2026") : null,
3356
- arts.list?.length === 0 ? (0, import_react2.createElement)(RN3.View, { style: { gap: 10, alignItems: "center", paddingVertical: 24 } }, k.muted(query.trim() ? `Nothing found for \u201C${query.trim()}\u201D.` : "No help articles yet."), k.btn("Send us a message", "primary", () => setView("messages"))) : null,
3357
- ...query.trim() ? arts.list?.length ? [card((arts.list ?? []).map(row), { padding: 8 })] : [] : [...groups].map(([name, items]) => (0, import_react2.createElement)(RN3.View, { key: name, style: { gap: 6 } }, k.muted(`${name} \xB7 ${items.length}`, { fontSize: 12, fontWeight: "600", paddingHorizontal: 2 }), card(items.map(row), { padding: 8 })))
3963
+ arts.list?.length === 0 ? (0, import_react3.createElement)(RN3.View, { style: { gap: 10, alignItems: "center", paddingVertical: 24 } }, k.muted(query.trim() ? `Nothing found for \u201C${query.trim()}\u201D.` : "No help articles yet."), k.btn("Send us a message", "primary", () => setView("messages"))) : null,
3964
+ ...query.trim() ? arts.list?.length ? [card((arts.list ?? []).map(row), { padding: 8 })] : [] : [...groups].map(([name, items]) => (0, import_react3.createElement)(RN3.View, { key: name, style: { gap: 6 } }, k.muted(`${name} \xB7 ${items.length}`, { fontSize: 12, fontWeight: "600", paddingHorizontal: 2 }), card(items.map(row), { padding: 8 })))
3358
3965
  );
3359
3966
  } else
3360
- content = (0, import_react2.createElement)(
3967
+ content = (0, import_react3.createElement)(
3361
3968
  RN3.ScrollView,
3362
3969
  { key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 32 } },
3363
- (0, import_react2.createElement)(ArticleView, { client, p, id: view.article, onAsk: () => setView("messages") })
3970
+ (0, import_react3.createElement)(ArticleView, { client, p, id: view.article, onAsk: () => setView("messages") })
3364
3971
  );
3365
3972
  const current = typeof view === "string" ? view : "help";
3366
3973
  const tab = (v, name, icon, badge = 0) => {
3367
3974
  const on = current === v;
3368
- return (0, import_react2.createElement)(
3975
+ return (0, import_react3.createElement)(
3369
3976
  RN3.Pressable,
3370
3977
  {
3371
3978
  key: v,
3372
3979
  onPress: () => setView(v),
3373
3980
  accessibilityRole: "tab",
3981
+ accessibilityLabel: name,
3374
3982
  accessibilityState: { selected: on },
3375
- style: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, height: 36, borderRadius: 18, ...on ? { backgroundColor: hu.segment, ...segLift } : {} }
3983
+ // Only the open tab carries its name; the others are icons, so the bar fits a narrow phone.
3984
+ style: { ...on ? { flex: 1 } : { width: 48 }, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, height: 36, borderRadius: 18, ...on ? { backgroundColor: hu.segment, ...segLift } : {} }
3376
3985
  },
3377
3986
  glyph(icon, on ? hu.fg : hu.muted, 17),
3378
- k.text(name, { fontSize: 13.5, fontWeight: "500", color: on ? hu.fg : hu.muted }),
3379
- badge ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: 7, right: 12, width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : null
3987
+ on ? k.text(name, { fontSize: 13.5, fontWeight: "500", color: hu.fg }) : null,
3988
+ badge ? (0, import_react3.createElement)(RN3.View, { style: { position: "absolute", top: 7, right: 12, width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : null
3380
3989
  );
3381
3990
  };
3382
3991
  const onAccent = view === "home" && !plain;
3383
3992
  const ground = onAccent ? p.accent : hu.bg;
3384
- const back = (to, label) => (0, import_react2.createElement)(
3993
+ const back = (to, label) => (0, import_react3.createElement)(
3385
3994
  RN3.Pressable,
3386
3995
  { key: "back", onPress: () => setView(to), accessibilityRole: "button", accessibilityLabel: label, hitSlop: 10, style: { width: 32, height: 32, alignItems: "center", justifyContent: "center", transform: [{ rotate: "180deg" }] } },
3387
3996
  glyph("next", hu.fg, 18)
3388
3997
  );
3389
- const head = (0, import_react2.createElement)(
3998
+ const head = (0, import_react3.createElement)(
3390
3999
  RN3.View,
3391
4000
  { key: "head", style: { flexDirection: "row", alignItems: "center", gap: 8, paddingLeft: 12, paddingRight: 12, paddingTop: 12, paddingBottom: 6, backgroundColor: ground } },
3392
- typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react2.createElement)(RN3.Image, { key: "logo", source: { uri: o.logo }, accessibilityIgnoresInvertColors: true, style: { width: 28, height: 28, borderRadius: 8, marginLeft: 4 } }) : teamMark(title, alpha(p.accent, 0.16), p.accent, 28) : null,
4001
+ typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react3.createElement)(RN3.Image, { key: "logo", source: { uri: o.logo }, accessibilityIgnoresInvertColors: true, resizeMode: "contain", style: { height: 28, aspectRatio: 1, maxWidth: 132, borderRadius: 8, marginLeft: 4 } }) : teamMark(title, alpha(p.accent, 0.16), p.accent, 28) : null,
3393
4002
  // In the chat: who answers, in the middle.
3394
- view === "messages" && title ? (0, import_react2.createElement)(
4003
+ view === "messages" && title ? (0, import_react3.createElement)(
3395
4004
  RN3.View,
3396
4005
  { style: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8 } },
3397
4006
  teamMark(title, alpha(p.accent, 0.16), p.accent, 28),
3398
- (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { flexShrink: 1, color: hu.fg, fontSize: 15, fontWeight: "600" } }, title)
3399
- ) : (0, import_react2.createElement)(RN3.View, { style: { flex: 1 } }),
3400
- (0, import_react2.createElement)(RN3.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: "Close", hitSlop: 12, style: { width: 32, height: 32, borderRadius: 10, alignItems: "center", justifyContent: "center" } }, glyph("cross", onAccent ? ink : p.muted, 18))
4007
+ (0, import_react3.createElement)(RN3.Text, { numberOfLines: 1, style: { flexShrink: 1, color: hu.fg, fontSize: 15, fontWeight: "600" } }, title)
4008
+ ) : (0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
4009
+ (0, import_react3.createElement)(RN3.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: "Close", hitSlop: 12, style: { width: 32, height: 32, borderRadius: 10, alignItems: "center", justifyContent: "center" } }, glyph("cross", onAccent ? ink : p.muted, 18))
3401
4010
  );
3402
- const powered = brand ? (0, import_react2.createElement)(
4011
+ const powered = brand ? (0, import_react3.createElement)(
3403
4012
  RN3.View,
3404
4013
  { key: "brand", style: { paddingTop: 2, paddingBottom: 6, alignItems: "center", backgroundColor: hu.bg } },
3405
- (0, import_react2.createElement)(
4014
+ (0, import_react3.createElement)(
3406
4015
  RN3.Text,
3407
4016
  { onPress: () => void RN3.Linking.openURL(BRAND_URL).catch(() => void 0), accessibilityRole: "link", hitSlop: 8, style: { color: p.muted, fontSize: 11.5 } },
3408
4017
  "Powered by ",
3409
- (0, import_react2.createElement)(RN3.Text, { style: { fontWeight: "600" } }, "MRRocket")
4018
+ (0, import_react3.createElement)(RN3.Text, { style: { fontWeight: "600" } }, "MRRocket")
3410
4019
  )
3411
4020
  ) : null;
3412
- return (0, import_react2.createElement)(
4021
+ return (0, import_react3.createElement)(
3413
4022
  RN3.Modal,
3414
4023
  { visible: true, animationType: "slide", presentationStyle: "pageSheet", onRequestClose: close },
3415
- (0, import_react2.createElement)(
4024
+ (0, import_react3.createElement)(
3416
4025
  RN3.KeyboardAvoidingView,
3417
4026
  { behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor: hu.bg } },
3418
4027
  head,
3419
4028
  content,
3420
- view === "messages" ? null : (0, import_react2.createElement)(
4029
+ view === "messages" ? null : (0, import_react3.createElement)(
3421
4030
  RN3.View,
3422
4031
  { key: "tabs", accessibilityRole: "tablist", style: { flexDirection: "row", marginHorizontal: 12, marginTop: 6, marginBottom: 6, padding: 4, borderRadius: 22, backgroundColor: hu.surface } },
3423
4032
  tab("home", "Home", "home"),
@@ -3425,13 +4034,13 @@ var MessengerSheet = ({
3425
4034
  showHelp ? tab("help", "Help", "help") : null
3426
4035
  ),
3427
4036
  powered,
3428
- (0, import_react2.createElement)(RN3.View, { style: { height: 16 } })
4037
+ (0, import_react3.createElement)(RN3.View, { style: { height: 16 } })
3429
4038
  )
3430
4039
  );
3431
4040
  };
3432
4041
  var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3433
- const [look, setLook] = (0, import_react2.useState)(null);
3434
- (0, import_react2.useEffect)(() => {
4042
+ const [look, setLook] = (0, import_react3.useState)(null);
4043
+ (0, import_react3.useEffect)(() => {
3435
4044
  const ctrl = new AbortController();
3436
4045
  void client.ready.then(
3437
4046
  () => client.api()?.messengerLook(ctrl.signal).then(setLook).catch(() => void 0)
@@ -3453,14 +4062,15 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3453
4062
  corners: look.corners,
3454
4063
  header: look.header,
3455
4064
  logo: look.logo,
4065
+ agentName: look.agentName,
3456
4066
  ...defined
3457
4067
  } : given;
3458
4068
  const p = usePalette({ theme: appearance?.theme ?? look?.theme, accent: appearance?.accent ?? look?.accent });
3459
4069
  const k = kit(p);
3460
4070
  const open = messengerOpen.useOpen();
3461
- const [view, setViewState] = (0, import_react2.useState)(msgView);
3462
- const [banner, setBanner] = (0, import_react2.useState)(null);
3463
- (0, import_react2.useEffect)(() => {
4071
+ const [view, setViewState] = (0, import_react3.useState)(msgView);
4072
+ const [banner, setBanner] = (0, import_react3.useState)(null);
4073
+ (0, import_react3.useEffect)(() => {
3464
4074
  if (open) setViewState(msgView);
3465
4075
  }, [open]);
3466
4076
  const setView = (v) => {
@@ -3469,13 +4079,13 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3469
4079
  };
3470
4080
  const chat = useChat(client, options, open, view);
3471
4081
  const latest = chat.unseen.at(-1);
3472
- (0, import_react2.useEffect)(() => {
4082
+ (0, import_react3.useEffect)(() => {
3473
4083
  if (!latest || open || options.preview === false) return;
3474
4084
  setBanner(latest);
3475
4085
  const t = setTimeout(() => setBanner(null), 6e3);
3476
4086
  return () => clearTimeout(t);
3477
4087
  }, [latest, open, options.preview]);
3478
- (0, import_react2.useEffect)(() => {
4088
+ (0, import_react3.useEffect)(() => {
3479
4089
  if (open) setBanner(null);
3480
4090
  }, [open]);
3481
4091
  const unseen = chat.unseen.length;
@@ -3487,10 +4097,10 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3487
4097
  ...edge === "top" ? { top: 60 } : edge === "bottom" ? { bottom: 34 } : { top: 0, bottom: 0, justifyContent: "center" },
3488
4098
  ...side === "left" ? { left: 20 } : side === "right" ? { right: 20 } : { left: 0, right: 0, alignItems: "center" }
3489
4099
  };
3490
- const launcher = options.launcher ? (0, import_react2.createElement)(
4100
+ const launcher = options.launcher ? (0, import_react3.createElement)(
3491
4101
  RN3.View,
3492
4102
  { key: "launcher", pointerEvents: "box-none", style: { position: "absolute", ...place } },
3493
- (0, import_react2.createElement)(
4103
+ (0, import_react3.createElement)(
3494
4104
  RN3.Pressable,
3495
4105
  {
3496
4106
  onPress: () => openAt(unseen ? "messages" : "home"),
@@ -3515,10 +4125,10 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3515
4125
  },
3516
4126
  glyph("chat", ink, label ? 20 : 26),
3517
4127
  label ? k.text(label, { color: ink, fontWeight: "700" }) : null,
3518
- unseen ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: -2, right: -2, minWidth: 20, height: 20, borderRadius: 10, backgroundColor: p.danger, alignItems: "center", justifyContent: "center", borderWidth: 2, borderColor: p.bg } }, k.text(String(Math.min(unseen, 9)), { color: "#fff", fontSize: 11, fontWeight: "700" })) : null
4128
+ unseen ? (0, import_react3.createElement)(RN3.View, { style: { position: "absolute", top: -2, right: -2, minWidth: 20, height: 20, borderRadius: 10, backgroundColor: p.danger, alignItems: "center", justifyContent: "center", borderWidth: 2, borderColor: p.bg } }, k.text(String(Math.min(unseen, 9)), { color: "#fff", fontSize: 11, fontWeight: "700" })) : null
3519
4129
  )
3520
4130
  ) : null;
3521
- const toast = banner ? (0, import_react2.createElement)(
4131
+ const toast = banner ? (0, import_react3.createElement)(
3522
4132
  RN3.Pressable,
3523
4133
  {
3524
4134
  key: "banner",
@@ -3542,30 +4152,30 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
3542
4152
  elevation: 6
3543
4153
  }
3544
4154
  },
3545
- k.muted(`${whoLabel[banner.from]}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
3546
- (0, import_react2.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15, lineHeight: 20 } }, banner.body)
4155
+ k.muted(`${who(banner.from, options.agentName)}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
4156
+ (0, import_react3.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15, lineHeight: 20 } }, banner.body)
3547
4157
  ) : null;
3548
4158
  if (!launcher && !toast && !open) return null;
3549
- return (0, import_react2.createElement)(
4159
+ return (0, import_react3.createElement)(
3550
4160
  RN3.View,
3551
4161
  { pointerEvents: "box-none", style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0 } },
3552
4162
  launcher,
3553
4163
  toast,
3554
- open ? (0, import_react2.createElement)(MessengerSheet, { client, p, o: options, chat, view, setView, brand: look?.branding !== false }) : null
4164
+ open ? (0, import_react3.createElement)(MessengerSheet, { client, p, o: options, chat, view, setView, brand: look?.branding !== false }) : null
3555
4165
  );
3556
4166
  };
3557
4167
 
3558
4168
  // src/native/index.tsx
3559
4169
  var RN4 = require("react-native");
3560
4170
  var MRRocketMask = ({ children, style }) => {
3561
- const ref = (0, import_react3.useRef)(null);
3562
- (0, import_react3.useEffect)(() => {
4171
+ const ref = (0, import_react4.useRef)(null);
4172
+ (0, import_react4.useEffect)(() => {
3563
4173
  maskedViews.add(ref);
3564
4174
  return () => {
3565
4175
  maskedViews.delete(ref);
3566
4176
  };
3567
4177
  }, []);
3568
- return (0, import_react3.createElement)(RN4.View, { ref, collapsable: false, style }, children);
4178
+ return (0, import_react4.createElement)(RN4.View, { ref, collapsable: false, style }, children);
3569
4179
  };
3570
4180
  var shared = null;
3571
4181
  var sharedKey = "";
@@ -3582,16 +4192,16 @@ var createMRRocket = (options) => {
3582
4192
  return shared;
3583
4193
  };
3584
4194
  var getMRRocket = () => shared;
3585
- var Ctx = (0, import_react3.createContext)(null);
4195
+ var Ctx = (0, import_react4.createContext)(null);
3586
4196
  var MRRocketProvider = ({ client: given, children, consent, flows = true, appearance, surveys = true, messenger = {}, feedback = {}, heatmaps = true, ...options }) => {
3587
- const [client] = (0, import_react3.useState)(() => given ?? createMRRocket({ ...options, consent }));
3588
- (0, import_react3.useEffect)(() => {
4197
+ const [client] = (0, import_react4.useState)(() => given ?? createMRRocket({ ...options, consent }));
4198
+ (0, import_react4.useEffect)(() => {
3589
4199
  if (consent) client.setConsent(consent);
3590
4200
  }, [client, consent]);
3591
- (0, import_react3.useEffect)(() => {
4201
+ (0, import_react4.useEffect)(() => {
3592
4202
  if (options.replay !== void 0) client.setReplay(options.replay);
3593
4203
  }, [client, options.replay]);
3594
- const start = (0, import_react3.useRef)(null);
4204
+ const start = (0, import_react4.useRef)(null);
3595
4205
  const touch = (e, end) => {
3596
4206
  const n = e.nativeEvent;
3597
4207
  const x = n.pageX ?? n.changedTouches?.[0]?.pageX;
@@ -3600,22 +4210,22 @@ var MRRocketProvider = ({ client: given, children, consent, flows = true, appear
3600
4210
  if (!end) start.current = { x, y, t: Date.now() };
3601
4211
  else if (start.current && Math.hypot(x - start.current.x, y - start.current.y) < 10 && Date.now() - start.current.t < 500) client.tap(x, y);
3602
4212
  };
3603
- return (0, import_react3.createElement)(
4213
+ return (0, import_react4.createElement)(
3604
4214
  Ctx.Provider,
3605
4215
  { value: client },
3606
- heatmaps ? (0, import_react3.createElement)(RN4.View, { style: { flex: 1 }, onTouchStartCapture: (e) => touch(e, false), onTouchEndCapture: (e) => touch(e, true) }, children) : children,
3607
- flows ? (0, import_react3.createElement)(MRRocketFlows, { flows: client.flows, paywall: client.paywall, appearance }) : null,
3608
- surveys ? (0, import_react3.createElement)(MRRocketSurveys, { client, appearance }) : null,
3609
- feedback ? (0, import_react3.createElement)(MRRocketFeedback, { client, appearance, options: feedback }) : null,
3610
- messenger ? (0, import_react3.createElement)(MRRocketMessenger, { client, appearance, options: messenger }) : null
4216
+ heatmaps ? (0, import_react4.createElement)(RN4.View, { style: { flex: 1 }, onTouchStartCapture: (e) => touch(e, false), onTouchEndCapture: (e) => touch(e, true) }, children) : children,
4217
+ flows ? (0, import_react4.createElement)(MRRocketFlows, { flows: client.flows, paywall: client.paywall, appearance }) : null,
4218
+ surveys ? (0, import_react4.createElement)(MRRocketSurveys, { client, appearance }) : null,
4219
+ feedback ? (0, import_react4.createElement)(MRRocketFeedback, { client, appearance, options: feedback }) : null,
4220
+ messenger ? (0, import_react4.createElement)(MRRocketMessenger, { client, appearance, options: messenger }) : null
3611
4221
  );
3612
4222
  };
3613
4223
  var useClient = () => {
3614
- const c = (0, import_react3.useContext)(Ctx);
4224
+ const c = (0, import_react4.useContext)(Ctx);
3615
4225
  if (!c) throw new Error("MRRocket: wrap your app in <MRRocketProvider apiKey=\u2026>.");
3616
4226
  return c;
3617
4227
  };
3618
- var useVersion = (client) => (0, import_react3.useSyncExternalStore)(client.subscribe, client.getVersion, client.getVersion);
4228
+ var useVersion = (client) => (0, import_react4.useSyncExternalStore)(client.subscribe, client.getVersion, client.getVersion);
3619
4229
  var useMRRocket = () => {
3620
4230
  const client = useClient();
3621
4231
  useVersion(client);
@@ -3649,9 +4259,9 @@ var useReview = () => {
3649
4259
  const client = useClient();
3650
4260
  return { request: client.requestReview };
3651
4261
  };
3652
- var useOnboarding = () => {
4262
+ var useOnboarding = (id) => {
3653
4263
  const client = useClient();
3654
- return { start: client.startOnboarding };
4264
+ return useOnboardingItem(client.flows, id, client.startOnboarding);
3655
4265
  };
3656
4266
  var useFlag = (key) => {
3657
4267
  const client = useClient();
@@ -3668,7 +4278,7 @@ var useVariant = (key) => {
3668
4278
  useVersion(client);
3669
4279
  const config = client.getConfig();
3670
4280
  const variant = client.peekVariant(key);
3671
- (0, import_react3.useEffect)(() => {
4281
+ (0, import_react4.useEffect)(() => {
3672
4282
  if (config) client.expose(key, variant);
3673
4283
  }, [client, config, key, variant]);
3674
4284
  return variant;
@@ -3679,7 +4289,7 @@ var useText = (id, fallback) => {
3679
4289
  const config = client.getConfig();
3680
4290
  const text2 = client.peekText(id);
3681
4291
  const person = client.getDistinctId();
3682
- (0, import_react3.useEffect)(() => {
4292
+ (0, import_react4.useEffect)(() => {
3683
4293
  if (config) client.exposeText(id);
3684
4294
  }, [client, config, id, person]);
3685
4295
  return text2 ?? fallback;
@@ -3704,13 +4314,13 @@ var useExpoRouterTracking = () => {
3704
4314
  console.warn("[MRRocket] useExpoRouterTracking needs expo-router. With React Navigation use useNavigationTracking.");
3705
4315
  }
3706
4316
  const pathname = (router?.usePathname ?? noPathname)();
3707
- (0, import_react3.useEffect)(() => {
4317
+ (0, import_react4.useEffect)(() => {
3708
4318
  if (pathname) client.screen(pathname);
3709
4319
  }, [client, pathname]);
3710
4320
  };
3711
4321
  var useNavigationTracking = () => {
3712
4322
  const client = useClient();
3713
- return (0, import_react3.useCallback)(
4323
+ return (0, import_react4.useCallback)(
3714
4324
  (state) => {
3715
4325
  const name = activeRouteName(state);
3716
4326
  if (name) client.screen(name);