@hook-sdk/template 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/AppRoot.tsx
2
2
  import { useMemo as useMemo3 } from "react";
3
3
  import { BrowserRouter, MemoryRouter, Navigate, Route, Routes } from "react-router-dom";
4
- import { useHook as useHook5 } from "@hook-sdk/sdk";
4
+ import { useHook as useHook6 } from "@hook-sdk/sdk";
5
5
 
6
6
  // src/config/AppConfigContext.tsx
7
7
  import { createContext, useContext } from "react";
@@ -77,7 +77,11 @@ var DeepLinksSchema = z.object({
77
77
  var AppConfigSchema = z.object({
78
78
  slug: z.string().regex(/^[a-z0-9-]+$/),
79
79
  name: z.string().min(1),
80
- branding: z.object({ primaryColor: z.string(), logoUrl: z.string().url() }),
80
+ branding: z.object({
81
+ primaryColor: z.string(),
82
+ logoUrl: z.string().url(),
83
+ iconUrl: z.string().url().optional()
84
+ }),
81
85
  authFlow: AuthFlowSchema,
82
86
  paywall: PaywallSchema,
83
87
  persistedKeys: z.array(PersistedKeySchema),
@@ -175,7 +179,7 @@ function ThemeProvider({ children }) {
175
179
 
176
180
  // src/hooks/usePaywallState.ts
177
181
  import { useCallback, useContext as useContext3, useMemo as useMemo2, useState } from "react";
178
- import { useHook as useHook2 } from "@hook-sdk/sdk";
182
+ import { useHook as useHook3 } from "@hook-sdk/sdk";
179
183
 
180
184
  // src/errors/asaas-pt-br.ts
181
185
  var MAP = {
@@ -196,6 +200,35 @@ function asaasErrorMessage(code) {
196
200
  return MAP[code] ?? "Ocorreu um erro inesperado. Tente novamente em instantes.";
197
201
  }
198
202
 
203
+ // src/hooks/usePaywallTracker.ts
204
+ import { useEffect as useEffect3, useRef } from "react";
205
+ import { useHook as useHook2 } from "@hook-sdk/sdk";
206
+ function deriveStep(s) {
207
+ if (s.pixPaid) return "success";
208
+ if (s.hasError) return "error";
209
+ if (s.pixPendingShown) return "pix_qr_shown";
210
+ if (s.selectedMethod === "card" && s.cpfValid) return "card_form";
211
+ if (s.cpfRequired && !s.cpfValid) return "cpf_input";
212
+ if (s.selectedMethod) return "method_select";
213
+ return "plan_select";
214
+ }
215
+ function usePaywallTracker(snapshot) {
216
+ const ctx = useHook2();
217
+ const track2 = typeof ctx.track === "function" ? ctx.track : void 0;
218
+ const lastStepRef = useRef(null);
219
+ const step = deriveStep(snapshot);
220
+ useEffect3(() => {
221
+ if (lastStepRef.current === step) return;
222
+ lastStepRef.current = step;
223
+ if (!track2) return;
224
+ track2("paywall_step_viewed", {
225
+ step,
226
+ method: snapshot.selectedMethod,
227
+ cycle: snapshot.cycle
228
+ });
229
+ }, [step]);
230
+ }
231
+
199
232
  // src/hooks/usePaywallState.ts
200
233
  function isCheckoutFailure(r) {
201
234
  return "ok" in r && r.ok === false;
@@ -210,7 +243,7 @@ var FALLBACK_PAYWALL = {
210
243
  };
211
244
  var isMethodAvailable = (availability, method) => availability[method] !== false;
212
245
  function usePaywallState() {
213
- const { subscription, plan } = useHook2();
246
+ const { subscription, plan } = useHook3();
214
247
  const configFromCtx = useContext3(AppConfigContext);
215
248
  const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
216
249
  const isFree = paywall.mode === "free";
@@ -439,6 +472,16 @@ function usePaywallState() {
439
472
  () => ({ required: cpfRequired, value: cpf, set: setCpf, valid: cpfValid }),
440
473
  [cpfRequired, cpf, cpfValid]
441
474
  );
475
+ usePaywallTracker({
476
+ selectedMethod,
477
+ cycle,
478
+ cpfRequired,
479
+ cpfValid,
480
+ pixPendingShown: pixPending !== null,
481
+ pixPaid: pixPending?.paid === true,
482
+ hasError: error !== null,
483
+ submitting
484
+ });
442
485
  return {
443
486
  // Subscription status (reactive, proxied from SDK)
444
487
  status,
@@ -493,10 +536,11 @@ function SubscriptionGate({ Paywall, children }) {
493
536
  }
494
537
 
495
538
  // src/components/InstallGate/InstallGate.tsx
496
- import { useEffect as useEffect4, useRef } from "react";
539
+ import { useEffect as useEffect5, useRef as useRef2 } from "react";
497
540
 
498
541
  // src/hooks/useInstallPrompt.ts
499
- import { useCallback as useCallback2, useEffect as useEffect3, useState as useState2 } from "react";
542
+ import { useCallback as useCallback2, useEffect as useEffect4, useState as useState2 } from "react";
543
+ var ANDROID_PROMPT_WAIT_MS = 3e3;
500
544
  var IOS_RE = /iPad|iPhone|iPod/;
501
545
  var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
502
546
  var ANDROID_RE = /Android/;
@@ -566,11 +610,12 @@ function track(event, props) {
566
610
  if (typeof window === "undefined") return;
567
611
  window.posthog?.capture?.(event, props);
568
612
  }
569
- function pickVariant(state) {
613
+ function pickVariant(state, promptWaitElapsed) {
570
614
  if (state.isInstalled) return "none";
571
615
  switch (state.platform) {
572
616
  case "android":
573
- return state.isInstallable ? "android-native" : "android-manual";
617
+ if (state.isInstallable) return "android-native";
618
+ return promptWaitElapsed ? "android-manual" : "android-pending";
574
619
  case "ios-safari":
575
620
  return "ios-safari";
576
621
  case "ios-other":
@@ -640,7 +685,16 @@ function useInstallPrompt(slug) {
640
685
  const [isDismissedSession, setIsDismissedSession] = useState2(() => readSessionSkip(slug));
641
686
  const [isDismissedPermanent, setIsDismissedPermanent] = useState2(() => readPermanentDismiss(slug).dismissed);
642
687
  const [skipCount, setSkipCount] = useState2(() => readSkipCount(slug));
643
- useEffect3(() => {
688
+ const [promptWaitElapsed, setPromptWaitElapsed] = useState2(() => {
689
+ if (typeof window === "undefined") return true;
690
+ return window.__pwaInstallPrompt != null;
691
+ });
692
+ useEffect4(() => {
693
+ if (promptWaitElapsed) return;
694
+ const id = setTimeout(() => setPromptWaitElapsed(true), ANDROID_PROMPT_WAIT_MS);
695
+ return () => clearTimeout(id);
696
+ }, [promptWaitElapsed]);
697
+ useEffect4(() => {
644
698
  if (typeof window === "undefined") return;
645
699
  if (window.__pwaInstallPrompt) {
646
700
  setIsInstallable(true);
@@ -664,7 +718,7 @@ function useInstallPrompt(slug) {
664
718
  window.removeEventListener("appinstalled", onInstalled);
665
719
  };
666
720
  }, [slug]);
667
- useEffect3(() => {
721
+ useEffect4(() => {
668
722
  if (typeof window === "undefined") return;
669
723
  const mq = window.matchMedia?.("(display-mode: standalone)");
670
724
  if (!mq) return;
@@ -677,17 +731,20 @@ function useInstallPrompt(slug) {
677
731
  mq.addEventListener?.("change", handler);
678
732
  return () => mq.removeEventListener?.("change", handler);
679
733
  }, [slug]);
680
- const variant = pickVariant({
681
- platform,
682
- iosBrowser,
683
- androidBrowser,
684
- inAppApp,
685
- isInstallable,
686
- isInstalled,
687
- isDismissedSession,
688
- isDismissedPermanent,
689
- skipCount
690
- });
734
+ const variant = pickVariant(
735
+ {
736
+ platform,
737
+ iosBrowser,
738
+ androidBrowser,
739
+ inAppApp,
740
+ isInstallable,
741
+ isInstalled,
742
+ isDismissedSession,
743
+ isDismissedPermanent,
744
+ skipCount
745
+ },
746
+ promptWaitElapsed
747
+ );
691
748
  const promptInstall = useCallback2(async () => {
692
749
  if (typeof window === "undefined") return false;
693
750
  const prompt = window.__pwaInstallPrompt;
@@ -1223,8 +1280,44 @@ function Step({ n, icon, children }) {
1223
1280
  );
1224
1281
  }
1225
1282
 
1283
+ // src/components/InstallGate/variants/AndroidPendingVariant.tsx
1284
+ import { jsx as jsx10 } from "react/jsx-runtime";
1285
+ function AndroidPendingVariant() {
1286
+ const copy = INSTALL_COPY.android.native;
1287
+ return /* @__PURE__ */ jsx10(InstallSplash, { title: copy.title, children: /* @__PURE__ */ jsx10(
1288
+ "div",
1289
+ {
1290
+ style: {
1291
+ display: "flex",
1292
+ flexDirection: "column",
1293
+ alignItems: "center",
1294
+ gap: 16,
1295
+ padding: "24px 0"
1296
+ },
1297
+ children: /* @__PURE__ */ jsx10(Spinner, {})
1298
+ }
1299
+ ) });
1300
+ }
1301
+ function Spinner() {
1302
+ return /* @__PURE__ */ jsx10(
1303
+ "div",
1304
+ {
1305
+ "aria-hidden": true,
1306
+ style: {
1307
+ width: 28,
1308
+ height: 28,
1309
+ borderRadius: "50%",
1310
+ border: "3px solid #e5e5e7",
1311
+ borderTopColor: "var(--hook-color-primary)",
1312
+ animation: "hook-install-spin 0.8s linear infinite"
1313
+ },
1314
+ children: /* @__PURE__ */ jsx10("style", { children: `@keyframes hook-install-spin { to { transform: rotate(360deg); } }` })
1315
+ }
1316
+ );
1317
+ }
1318
+
1226
1319
  // src/components/InstallGate/Step.tsx
1227
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
1320
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1228
1321
  function Step2({
1229
1322
  n,
1230
1323
  title,
@@ -1242,7 +1335,7 @@ function Step2({
1242
1335
  textAlign: "left"
1243
1336
  },
1244
1337
  children: [
1245
- /* @__PURE__ */ jsx10(
1338
+ /* @__PURE__ */ jsx11(
1246
1339
  "div",
1247
1340
  {
1248
1341
  style: {
@@ -1262,8 +1355,8 @@ function Step2({
1262
1355
  }
1263
1356
  ),
1264
1357
  /* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
1265
- /* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
1266
- subtitle && /* @__PURE__ */ jsx10("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
1358
+ /* @__PURE__ */ jsx11("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
1359
+ subtitle && /* @__PURE__ */ jsx11("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
1267
1360
  visual
1268
1361
  ] })
1269
1362
  ]
@@ -1272,7 +1365,7 @@ function Step2({
1272
1365
  }
1273
1366
 
1274
1367
  // src/components/InstallGate/variants/IOSafariVariant.tsx
1275
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
1368
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1276
1369
  function IOSafariVariant({
1277
1370
  state,
1278
1371
  actions
@@ -1280,13 +1373,13 @@ function IOSafariVariant({
1280
1373
  const copy = INSTALL_COPY.iosSafari;
1281
1374
  const showPermanent = shouldShowPermanentOption(state);
1282
1375
  return /* @__PURE__ */ jsxs6(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
1283
- /* @__PURE__ */ jsx11(
1376
+ /* @__PURE__ */ jsx12(
1284
1377
  Step2,
1285
1378
  {
1286
1379
  n: 1,
1287
1380
  title: copy.step1.title,
1288
1381
  subtitle: copy.step1.subtitle,
1289
- visual: /* @__PURE__ */ jsx11(
1382
+ visual: /* @__PURE__ */ jsx12(
1290
1383
  "div",
1291
1384
  {
1292
1385
  style: {
@@ -1298,12 +1391,12 @@ function IOSafariVariant({
1298
1391
  padding: "12px 0",
1299
1392
  marginTop: 8
1300
1393
  },
1301
- children: /* @__PURE__ */ jsx11(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
1394
+ children: /* @__PURE__ */ jsx12(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
1302
1395
  }
1303
1396
  )
1304
1397
  }
1305
1398
  ),
1306
- /* @__PURE__ */ jsx11(
1399
+ /* @__PURE__ */ jsx12(
1307
1400
  Step2,
1308
1401
  {
1309
1402
  n: 2,
@@ -1321,19 +1414,19 @@ function IOSafariVariant({
1321
1414
  marginTop: 8
1322
1415
  },
1323
1416
  children: [
1324
- /* @__PURE__ */ jsx11(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1325
- /* @__PURE__ */ jsx11("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1417
+ /* @__PURE__ */ jsx12(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1418
+ /* @__PURE__ */ jsx12("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1326
1419
  ]
1327
1420
  }
1328
1421
  )
1329
1422
  }
1330
1423
  ),
1331
- /* @__PURE__ */ jsx11(
1424
+ /* @__PURE__ */ jsx12(
1332
1425
  Step2,
1333
1426
  {
1334
1427
  n: 3,
1335
1428
  title: copy.step3.title,
1336
- visual: /* @__PURE__ */ jsx11(
1429
+ visual: /* @__PURE__ */ jsx12(
1337
1430
  "div",
1338
1431
  {
1339
1432
  style: {
@@ -1344,7 +1437,7 @@ function IOSafariVariant({
1344
1437
  padding: "10px 14px",
1345
1438
  marginTop: 8
1346
1439
  },
1347
- children: /* @__PURE__ */ jsx11(
1440
+ children: /* @__PURE__ */ jsx12(
1348
1441
  "span",
1349
1442
  {
1350
1443
  style: {
@@ -1359,7 +1452,7 @@ function IOSafariVariant({
1359
1452
  )
1360
1453
  }
1361
1454
  ),
1362
- /* @__PURE__ */ jsx11(
1455
+ /* @__PURE__ */ jsx12(
1363
1456
  "button",
1364
1457
  {
1365
1458
  "data-testid": "install-prompt-skip-session",
@@ -1369,7 +1462,7 @@ function IOSafariVariant({
1369
1462
  children: copy.skip
1370
1463
  }
1371
1464
  ),
1372
- showPermanent && /* @__PURE__ */ jsx11(
1465
+ showPermanent && /* @__PURE__ */ jsx12(
1373
1466
  "button",
1374
1467
  {
1375
1468
  "data-testid": "install-prompt-skip-permanent",
@@ -1383,7 +1476,7 @@ function IOSafariVariant({
1383
1476
  }
1384
1477
 
1385
1478
  // src/components/InstallGate/variants/IOSOtherVariant.tsx
1386
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1479
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1387
1480
  function IOSOtherVariant({
1388
1481
  state,
1389
1482
  actions
@@ -1391,13 +1484,13 @@ function IOSOtherVariant({
1391
1484
  const copy = INSTALL_COPY.iosOther;
1392
1485
  const showPermanent = shouldShowPermanentOption(state);
1393
1486
  return /* @__PURE__ */ jsxs7(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
1394
- /* @__PURE__ */ jsx12(
1487
+ /* @__PURE__ */ jsx13(
1395
1488
  Step2,
1396
1489
  {
1397
1490
  n: 1,
1398
1491
  title: copy.step1.title,
1399
1492
  subtitle: copy.step1.subtitle,
1400
- visual: /* @__PURE__ */ jsx12(
1493
+ visual: /* @__PURE__ */ jsx13(
1401
1494
  "div",
1402
1495
  {
1403
1496
  style: {
@@ -1409,12 +1502,12 @@ function IOSOtherVariant({
1409
1502
  padding: "12px 0",
1410
1503
  marginTop: 8
1411
1504
  },
1412
- children: /* @__PURE__ */ jsx12(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
1505
+ children: /* @__PURE__ */ jsx13(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
1413
1506
  }
1414
1507
  )
1415
1508
  }
1416
1509
  ),
1417
- /* @__PURE__ */ jsx12(
1510
+ /* @__PURE__ */ jsx13(
1418
1511
  Step2,
1419
1512
  {
1420
1513
  n: 2,
@@ -1432,19 +1525,19 @@ function IOSOtherVariant({
1432
1525
  marginTop: 8
1433
1526
  },
1434
1527
  children: [
1435
- /* @__PURE__ */ jsx12(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1436
- /* @__PURE__ */ jsx12("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1528
+ /* @__PURE__ */ jsx13(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1529
+ /* @__PURE__ */ jsx13("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1437
1530
  ]
1438
1531
  }
1439
1532
  )
1440
1533
  }
1441
1534
  ),
1442
- /* @__PURE__ */ jsx12(
1535
+ /* @__PURE__ */ jsx13(
1443
1536
  Step2,
1444
1537
  {
1445
1538
  n: 3,
1446
1539
  title: copy.step3.title,
1447
- visual: /* @__PURE__ */ jsx12(
1540
+ visual: /* @__PURE__ */ jsx13(
1448
1541
  "div",
1449
1542
  {
1450
1543
  style: {
@@ -1455,7 +1548,7 @@ function IOSOtherVariant({
1455
1548
  padding: "10px 14px",
1456
1549
  marginTop: 8
1457
1550
  },
1458
- children: /* @__PURE__ */ jsx12(
1551
+ children: /* @__PURE__ */ jsx13(
1459
1552
  "span",
1460
1553
  {
1461
1554
  style: {
@@ -1470,7 +1563,7 @@ function IOSOtherVariant({
1470
1563
  )
1471
1564
  }
1472
1565
  ),
1473
- /* @__PURE__ */ jsx12(
1566
+ /* @__PURE__ */ jsx13(
1474
1567
  "button",
1475
1568
  {
1476
1569
  "data-testid": "install-prompt-skip-session",
@@ -1480,7 +1573,7 @@ function IOSOtherVariant({
1480
1573
  children: copy.skip
1481
1574
  }
1482
1575
  ),
1483
- showPermanent && /* @__PURE__ */ jsx12(
1576
+ showPermanent && /* @__PURE__ */ jsx13(
1484
1577
  "button",
1485
1578
  {
1486
1579
  "data-testid": "install-prompt-skip-permanent",
@@ -1495,7 +1588,7 @@ function IOSOtherVariant({
1495
1588
 
1496
1589
  // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1497
1590
  import { useState as useState3 } from "react";
1498
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1591
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1499
1592
  function InAppBrowserVariant({
1500
1593
  state,
1501
1594
  actions
@@ -1512,9 +1605,9 @@ function InAppBrowserVariant({
1512
1605
  };
1513
1606
  const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
1514
1607
  return /* @__PURE__ */ jsxs8(InstallSplash, { title: appCopy.title, children: [
1515
- /* @__PURE__ */ jsx13(Step3, { n: 1, icon: /* @__PURE__ */ jsx13(DotsIcon, { size: 20 }), children: appCopy.step1 }),
1516
- /* @__PURE__ */ jsx13(Step3, { n: 2, icon: /* @__PURE__ */ jsx13(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
1517
- /* @__PURE__ */ jsx13(
1608
+ /* @__PURE__ */ jsx14(Step3, { n: 1, icon: /* @__PURE__ */ jsx14(DotsIcon, { size: 20 }), children: appCopy.step1 }),
1609
+ /* @__PURE__ */ jsx14(Step3, { n: 2, icon: /* @__PURE__ */ jsx14(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
1610
+ /* @__PURE__ */ jsx14(
1518
1611
  "button",
1519
1612
  {
1520
1613
  "data-testid": "install-prompt-cta-inapp-copy",
@@ -1524,7 +1617,7 @@ function InAppBrowserVariant({
1524
1617
  children: copied ? copy.copiedToast : copy.copy
1525
1618
  }
1526
1619
  ),
1527
- /* @__PURE__ */ jsx13(
1620
+ /* @__PURE__ */ jsx14(
1528
1621
  "button",
1529
1622
  {
1530
1623
  "data-testid": "install-prompt-skip-session",
@@ -1534,7 +1627,7 @@ function InAppBrowserVariant({
1534
1627
  children: copy.skip
1535
1628
  }
1536
1629
  ),
1537
- showPermanent && /* @__PURE__ */ jsx13(
1630
+ showPermanent && /* @__PURE__ */ jsx14(
1538
1631
  "button",
1539
1632
  {
1540
1633
  "data-testid": "install-prompt-skip-permanent",
@@ -1565,7 +1658,7 @@ function Step3({
1565
1658
  textAlign: "left"
1566
1659
  },
1567
1660
  children: [
1568
- /* @__PURE__ */ jsx13(
1661
+ /* @__PURE__ */ jsx14(
1569
1662
  "div",
1570
1663
  {
1571
1664
  style: {
@@ -1584,15 +1677,15 @@ function Step3({
1584
1677
  children: n
1585
1678
  }
1586
1679
  ),
1587
- /* @__PURE__ */ jsx13("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
1588
- /* @__PURE__ */ jsx13("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
1680
+ /* @__PURE__ */ jsx14("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
1681
+ /* @__PURE__ */ jsx14("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
1589
1682
  ]
1590
1683
  }
1591
1684
  );
1592
1685
  }
1593
1686
 
1594
1687
  // src/components/InstallGate/variants/DesktopVariant.tsx
1595
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
1688
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1596
1689
  function DesktopVariant({
1597
1690
  state,
1598
1691
  actions
@@ -1608,14 +1701,14 @@ function DesktopVariant({
1608
1701
  "aria-label": copy.title,
1609
1702
  style: bannerStyle,
1610
1703
  children: [
1611
- iconUrl ? /* @__PURE__ */ jsx14(
1704
+ iconUrl ? /* @__PURE__ */ jsx15(
1612
1705
  "img",
1613
1706
  {
1614
1707
  src: iconUrl,
1615
1708
  alt: "",
1616
1709
  style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
1617
1710
  }
1618
- ) : /* @__PURE__ */ jsx14(
1711
+ ) : /* @__PURE__ */ jsx15(
1619
1712
  "div",
1620
1713
  {
1621
1714
  style: {
@@ -1635,8 +1728,8 @@ function DesktopVariant({
1635
1728
  }
1636
1729
  ),
1637
1730
  /* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
1638
- /* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
1639
- /* @__PURE__ */ jsx14("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
1731
+ /* @__PURE__ */ jsx15("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
1732
+ /* @__PURE__ */ jsx15("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
1640
1733
  ] }),
1641
1734
  /* @__PURE__ */ jsxs9(
1642
1735
  "button",
@@ -1659,12 +1752,12 @@ function DesktopVariant({
1659
1752
  flexShrink: 0
1660
1753
  },
1661
1754
  children: [
1662
- /* @__PURE__ */ jsx14(DownloadIcon, { size: 14 }),
1755
+ /* @__PURE__ */ jsx15(DownloadIcon, { size: 14 }),
1663
1756
  copy.cta
1664
1757
  ]
1665
1758
  }
1666
1759
  ),
1667
- /* @__PURE__ */ jsx14(
1760
+ /* @__PURE__ */ jsx15(
1668
1761
  "button",
1669
1762
  {
1670
1763
  "data-testid": "install-prompt-desktop-close",
@@ -1679,7 +1772,7 @@ function DesktopVariant({
1679
1772
  padding: 4,
1680
1773
  flexShrink: 0
1681
1774
  },
1682
- children: /* @__PURE__ */ jsx14(XIcon, { size: 16 })
1775
+ children: /* @__PURE__ */ jsx15(XIcon, { size: 16 })
1683
1776
  }
1684
1777
  )
1685
1778
  ]
@@ -1703,14 +1796,14 @@ var bannerStyle = {
1703
1796
  };
1704
1797
 
1705
1798
  // src/components/InstallGate/InstallGate.tsx
1706
- import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1799
+ import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1707
1800
  function InstallGate({ children }) {
1708
1801
  const { slug, features_enabled } = useTemplateConfig();
1709
1802
  const enabled = features_enabled.includes("install_prompt");
1710
1803
  const installState = useInstallPrompt(slug);
1711
1804
  const shouldBlock = enabled && shouldBlockInstall(installState);
1712
- const trackedRef = useRef(null);
1713
- useEffect4(() => {
1805
+ const trackedRef = useRef2(null);
1806
+ useEffect5(() => {
1714
1807
  if (!shouldBlock) return;
1715
1808
  if (typeof window === "undefined") return;
1716
1809
  const variantKey = `${slug}:${installState.variant}`;
@@ -1724,30 +1817,32 @@ function InstallGate({ children }) {
1724
1817
  variant: installState.variant
1725
1818
  });
1726
1819
  }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
1727
- if (!enabled) return /* @__PURE__ */ jsx15(Fragment3, { children });
1728
- if (installState.isInstalled) return /* @__PURE__ */ jsx15(Fragment3, { children });
1820
+ if (!enabled) return /* @__PURE__ */ jsx16(Fragment3, { children });
1821
+ if (installState.isInstalled) return /* @__PURE__ */ jsx16(Fragment3, { children });
1729
1822
  if (installState.variant === "desktop") {
1730
1823
  const showBanner = !installState.isDismissedSession && !installState.isDismissedPermanent;
1731
1824
  return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1732
1825
  children,
1733
- showBanner && /* @__PURE__ */ jsx15(DesktopVariant, { state: installState, actions: installState })
1826
+ showBanner && /* @__PURE__ */ jsx16(DesktopVariant, { state: installState, actions: installState })
1734
1827
  ] });
1735
1828
  }
1736
- if (!shouldBlock) return /* @__PURE__ */ jsx15(Fragment3, { children });
1829
+ if (!shouldBlock) return /* @__PURE__ */ jsx16(Fragment3, { children });
1737
1830
  switch (installState.variant) {
1738
1831
  case "android-native":
1739
- return /* @__PURE__ */ jsx15(AndroidNativeVariant, { state: installState, actions: installState });
1832
+ return /* @__PURE__ */ jsx16(AndroidNativeVariant, { state: installState, actions: installState });
1740
1833
  case "android-manual":
1741
- return /* @__PURE__ */ jsx15(AndroidManualVariant, { state: installState, actions: installState });
1834
+ return /* @__PURE__ */ jsx16(AndroidManualVariant, { state: installState, actions: installState });
1835
+ case "android-pending":
1836
+ return /* @__PURE__ */ jsx16(AndroidPendingVariant, {});
1742
1837
  case "ios-safari":
1743
- return /* @__PURE__ */ jsx15(IOSafariVariant, { state: installState, actions: installState });
1838
+ return /* @__PURE__ */ jsx16(IOSafariVariant, { state: installState, actions: installState });
1744
1839
  case "ios-other":
1745
- return /* @__PURE__ */ jsx15(IOSOtherVariant, { state: installState, actions: installState });
1840
+ return /* @__PURE__ */ jsx16(IOSOtherVariant, { state: installState, actions: installState });
1746
1841
  case "in-app":
1747
- return /* @__PURE__ */ jsx15(InAppBrowserVariant, { state: installState, actions: installState });
1842
+ return /* @__PURE__ */ jsx16(InAppBrowserVariant, { state: installState, actions: installState });
1748
1843
  case "none":
1749
1844
  default:
1750
- return /* @__PURE__ */ jsx15(Fragment3, { children });
1845
+ return /* @__PURE__ */ jsx16(Fragment3, { children });
1751
1846
  }
1752
1847
  }
1753
1848
 
@@ -1757,16 +1852,16 @@ function PushPrompt() {
1757
1852
  }
1758
1853
 
1759
1854
  // src/internal/SessionExpiredBanner.tsx
1760
- import { useEffect as useEffect5, useRef as useRef2, useState as useState4 } from "react";
1761
- import { useHook as useHook3 } from "@hook-sdk/sdk";
1762
- import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1855
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState4 } from "react";
1856
+ import { useHook as useHook4 } from "@hook-sdk/sdk";
1857
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
1763
1858
  var DISMISS_KEY = "hook:session-expired-dismissed-until";
1764
1859
  var DISMISS_TTL_MS = 60 * 60 * 1e3;
1765
1860
  function SessionExpiredBanner() {
1766
- const { authStatus } = useHook3();
1767
- const wasAuthRef = useRef2(false);
1861
+ const { authStatus } = useHook4();
1862
+ const wasAuthRef = useRef3(false);
1768
1863
  const [show, setShow] = useState4(false);
1769
- useEffect5(() => {
1864
+ useEffect6(() => {
1770
1865
  if (authStatus === "authenticated") {
1771
1866
  wasAuthRef.current = true;
1772
1867
  setShow(false);
@@ -1788,11 +1883,11 @@ function SessionExpiredBanner() {
1788
1883
  }
1789
1884
  return /* @__PURE__ */ jsxs11("div", { role: "alert", className: "fixed top-0 inset-x-0 bg-red-600 text-white px-4 py-2 flex items-center justify-between gap-3 text-sm shadow", style: { zIndex: 10001 }, children: [
1790
1885
  /* @__PURE__ */ jsxs11("span", { children: [
1791
- /* @__PURE__ */ jsx16("strong", { children: "Sua sess\xE3o expirou." }),
1886
+ /* @__PURE__ */ jsx17("strong", { children: "Sua sess\xE3o expirou." }),
1792
1887
  " Fa\xE7a login novamente para continuar."
1793
1888
  ] }),
1794
1889
  /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1795
- /* @__PURE__ */ jsx16(
1890
+ /* @__PURE__ */ jsx17(
1796
1891
  "button",
1797
1892
  {
1798
1893
  type: "button",
@@ -1801,7 +1896,7 @@ function SessionExpiredBanner() {
1801
1896
  children: "Fazer login"
1802
1897
  }
1803
1898
  ),
1804
- /* @__PURE__ */ jsx16(
1899
+ /* @__PURE__ */ jsx17(
1805
1900
  "button",
1806
1901
  {
1807
1902
  type: "button",
@@ -1817,7 +1912,7 @@ function SessionExpiredBanner() {
1817
1912
 
1818
1913
  // src/defaults/ErrorBoundary.tsx
1819
1914
  import { Component } from "react";
1820
- import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
1915
+ import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1821
1916
  var ErrorBoundary = class extends Component {
1822
1917
  state = { error: null };
1823
1918
  static getDerivedStateFromError(error) {
@@ -1836,27 +1931,27 @@ var ErrorBoundary = class extends Component {
1836
1931
  render() {
1837
1932
  if (this.state.error) {
1838
1933
  return this.props.fallback ?? /* @__PURE__ */ jsxs12("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
1839
- /* @__PURE__ */ jsx17("h2", { children: "Algo deu errado" }),
1840
- /* @__PURE__ */ jsx17("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
1934
+ /* @__PURE__ */ jsx18("h2", { children: "Algo deu errado" }),
1935
+ /* @__PURE__ */ jsx18("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
1841
1936
  ] });
1842
1937
  }
1843
- return /* @__PURE__ */ jsx17(Fragment4, { children: this.props.children });
1938
+ return /* @__PURE__ */ jsx18(Fragment4, { children: this.props.children });
1844
1939
  }
1845
1940
  };
1846
1941
 
1847
1942
  // src/internal/PaymentReturnHandler.tsx
1848
- import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef3, useState as useState5 } from "react";
1849
- import { useHook as useHook4 } from "@hook-sdk/sdk";
1850
- import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1943
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef4, useState as useState5 } from "react";
1944
+ import { useHook as useHook5 } from "@hook-sdk/sdk";
1945
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1851
1946
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
1852
1947
  var MAX_CYCLES = 3;
1853
1948
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
1854
1949
  function PaymentReturnHandler({ children }) {
1855
- const { subscription } = useHook4();
1856
- const subRef = useRef3(subscription);
1950
+ const { subscription } = useHook5();
1951
+ const subRef = useRef4(subscription);
1857
1952
  subRef.current = subscription;
1858
- const runIdRef = useRef3(0);
1859
- const cyclesRef = useRef3(0);
1953
+ const runIdRef = useRef4(0);
1954
+ const cyclesRef = useRef4(0);
1860
1955
  const [state, setState] = useState5("idle");
1861
1956
  const runPoll = useCallback3(() => {
1862
1957
  const runId = ++runIdRef.current;
@@ -1893,7 +1988,7 @@ function PaymentReturnHandler({ children }) {
1893
1988
  };
1894
1989
  void tick();
1895
1990
  }, []);
1896
- useEffect6(() => {
1991
+ useEffect7(() => {
1897
1992
  if (typeof window === "undefined") return;
1898
1993
  const url = new URL(window.location.href);
1899
1994
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -1910,19 +2005,19 @@ function PaymentReturnHandler({ children }) {
1910
2005
  window.location.href = cleanUrl.toString();
1911
2006
  }, []);
1912
2007
  if (state === "confirming") {
1913
- return /* @__PURE__ */ jsx18("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2008
+ return /* @__PURE__ */ jsx19("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
1914
2009
  }
1915
2010
  if (state === "waiting") {
1916
- return /* @__PURE__ */ jsx18("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
1917
- /* @__PURE__ */ jsx18("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
1918
- /* @__PURE__ */ jsx18("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2011
+ return /* @__PURE__ */ jsx19("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2012
+ /* @__PURE__ */ jsx19("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2013
+ /* @__PURE__ */ jsx19("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
1919
2014
  ] }) });
1920
2015
  }
1921
2016
  if (state === "timeout") {
1922
- return /* @__PURE__ */ jsx18("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
1923
- /* @__PURE__ */ jsx18("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
2017
+ return /* @__PURE__ */ jsx19("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2018
+ /* @__PURE__ */ jsx19("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
1924
2019
  /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1925
- /* @__PURE__ */ jsx18(
2020
+ /* @__PURE__ */ jsx19(
1926
2021
  "button",
1927
2022
  {
1928
2023
  type: "button",
@@ -1935,7 +2030,7 @@ function PaymentReturnHandler({ children }) {
1935
2030
  children: "Tentar de novo"
1936
2031
  }
1937
2032
  ),
1938
- /* @__PURE__ */ jsx18(
2033
+ /* @__PURE__ */ jsx19(
1939
2034
  "button",
1940
2035
  {
1941
2036
  type: "button",
@@ -1945,7 +2040,7 @@ function PaymentReturnHandler({ children }) {
1945
2040
  children: "Voltar pro app"
1946
2041
  }
1947
2042
  ),
1948
- /* @__PURE__ */ jsx18(
2043
+ /* @__PURE__ */ jsx19(
1949
2044
  "a",
1950
2045
  {
1951
2046
  href: SUPPORT_MAILTO,
@@ -1957,7 +2052,7 @@ function PaymentReturnHandler({ children }) {
1957
2052
  ] })
1958
2053
  ] }) });
1959
2054
  }
1960
- return /* @__PURE__ */ jsx18(Fragment5, { children });
2055
+ return /* @__PURE__ */ jsx19(Fragment5, { children });
1961
2056
  }
1962
2057
  var overlayStyle2 = {
1963
2058
  position: "fixed",
@@ -1996,7 +2091,7 @@ var linkStyle = {
1996
2091
  };
1997
2092
 
1998
2093
  // src/AppRoot.tsx
1999
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2094
+ import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
2000
2095
  function buildLegacyConfigShim(config) {
2001
2096
  const paywall = config.paywall;
2002
2097
  const isFree = paywall.mode === "free";
@@ -2006,7 +2101,16 @@ function buildLegacyConfigShim(config) {
2006
2101
  slug: config.slug,
2007
2102
  name: config.name,
2008
2103
  email_alias: config.slug,
2009
- theme: { primary_color: config.branding.primaryColor },
2104
+ // Map branding into the legacy theme shape so InstallSplash (and
2105
+ // anything else reading theme.icon_url / theme.logo_url) can surface
2106
+ // the app icon instead of the generic "first letter of name" fallback.
2107
+ // Falls back to logoUrl when iconUrl is unset — apps that haven't
2108
+ // adopted iconUrl keep their previous behavior unchanged.
2109
+ theme: {
2110
+ primary_color: config.branding.primaryColor,
2111
+ icon_url: config.branding.iconUrl ?? config.branding.logoUrl,
2112
+ logo_url: config.branding.logoUrl
2113
+ },
2010
2114
  features_enabled: config.features_enabled ?? [],
2011
2115
  dependencies_allowlist: ["react", "react-dom"],
2012
2116
  subscription: {
@@ -2064,10 +2168,10 @@ function AppRoot(props) {
2064
2168
  const Router = testRouter === "memory" ? MemoryRouter : BrowserRouter;
2065
2169
  const basename = `/app/${config.slug}`;
2066
2170
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
2067
- return /* @__PURE__ */ jsx19(ErrorBoundary, { children: /* @__PURE__ */ jsx19(AppConfigProvider, { config, children: /* @__PURE__ */ jsx19(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx19(ThemeProvider, { children: /* @__PURE__ */ jsx19(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
2068
- /* @__PURE__ */ jsx19(DeepLinkHandler, { deepLinks: config.deepLinks }),
2069
- /* @__PURE__ */ jsx19(SessionExpiredBanner, {}),
2070
- /* @__PURE__ */ jsx19(InstallGate, { children: /* @__PURE__ */ jsx19(
2171
+ return /* @__PURE__ */ jsx20(ErrorBoundary, { children: /* @__PURE__ */ jsx20(AppConfigProvider, { config, children: /* @__PURE__ */ jsx20(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx20(ThemeProvider, { children: /* @__PURE__ */ jsx20(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
2172
+ /* @__PURE__ */ jsx20(DeepLinkHandler, { deepLinks: config.deepLinks }),
2173
+ /* @__PURE__ */ jsx20(SessionExpiredBanner, {}),
2174
+ /* @__PURE__ */ jsx20(InstallGate, { children: /* @__PURE__ */ jsx20(
2071
2175
  AuthGated,
2072
2176
  {
2073
2177
  config,
@@ -2081,7 +2185,7 @@ function AppRoot(props) {
2081
2185
  PreAuthFlow,
2082
2186
  children: /* @__PURE__ */ jsxs14(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: [
2083
2187
  children,
2084
- /* @__PURE__ */ jsx19(PushPrompt, {})
2188
+ /* @__PURE__ */ jsx20(PushPrompt, {})
2085
2189
  ] })
2086
2190
  }
2087
2191
  ) })
@@ -2097,37 +2201,37 @@ function AuthGated({
2097
2201
  EmailVerify,
2098
2202
  PreAuthFlow
2099
2203
  }) {
2100
- const { authStatus } = useHook5();
2204
+ const { authStatus } = useHook6();
2101
2205
  if (authStatus === "loading") return null;
2102
2206
  if (authStatus !== "authenticated") {
2103
2207
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
2104
2208
  return /* @__PURE__ */ jsxs14(Routes, { children: [
2105
- /* @__PURE__ */ jsx19(Route, { path: "/signin", element: /* @__PURE__ */ jsx19(Login, {}) }),
2106
- /* @__PURE__ */ jsx19(Route, { path: "/signup", element: /* @__PURE__ */ jsx19(Signup, {}) }),
2107
- /* @__PURE__ */ jsx19(Route, { path: "/forgot", element: /* @__PURE__ */ jsx19(Forgot, {}) }),
2108
- /* @__PURE__ */ jsx19(Route, { path: "/reset", element: /* @__PURE__ */ jsx19(Reset, {}) }),
2109
- EmailVerify ? /* @__PURE__ */ jsx19(Route, { path: "/verify", element: /* @__PURE__ */ jsx19(EmailVerify, {}) }) : null,
2110
- /* @__PURE__ */ jsx19(Route, { path: "/*", element: /* @__PURE__ */ jsx19(PreAuthFlow, {}) })
2209
+ /* @__PURE__ */ jsx20(Route, { path: "/signin", element: /* @__PURE__ */ jsx20(Login, {}) }),
2210
+ /* @__PURE__ */ jsx20(Route, { path: "/signup", element: /* @__PURE__ */ jsx20(Signup, {}) }),
2211
+ /* @__PURE__ */ jsx20(Route, { path: "/forgot", element: /* @__PURE__ */ jsx20(Forgot, {}) }),
2212
+ /* @__PURE__ */ jsx20(Route, { path: "/reset", element: /* @__PURE__ */ jsx20(Reset, {}) }),
2213
+ EmailVerify ? /* @__PURE__ */ jsx20(Route, { path: "/verify", element: /* @__PURE__ */ jsx20(EmailVerify, {}) }) : null,
2214
+ /* @__PURE__ */ jsx20(Route, { path: "/*", element: /* @__PURE__ */ jsx20(PreAuthFlow, {}) })
2111
2215
  ] });
2112
2216
  }
2113
2217
  return /* @__PURE__ */ jsxs14(Routes, { children: [
2114
- /* @__PURE__ */ jsx19(Route, { path: "/", element: /* @__PURE__ */ jsx19(Login, {}) }),
2115
- /* @__PURE__ */ jsx19(Route, { path: "/signup", element: /* @__PURE__ */ jsx19(Signup, {}) }),
2116
- /* @__PURE__ */ jsx19(Route, { path: "/forgot", element: /* @__PURE__ */ jsx19(Forgot, {}) }),
2117
- /* @__PURE__ */ jsx19(Route, { path: "/reset", element: /* @__PURE__ */ jsx19(Reset, {}) }),
2118
- EmailVerify ? /* @__PURE__ */ jsx19(Route, { path: "/verify", element: /* @__PURE__ */ jsx19(EmailVerify, {}) }) : null,
2119
- /* @__PURE__ */ jsx19(Route, { path: "*", element: /* @__PURE__ */ jsx19(Navigate, { to: "/", replace: true }) })
2218
+ /* @__PURE__ */ jsx20(Route, { path: "/", element: /* @__PURE__ */ jsx20(Login, {}) }),
2219
+ /* @__PURE__ */ jsx20(Route, { path: "/signup", element: /* @__PURE__ */ jsx20(Signup, {}) }),
2220
+ /* @__PURE__ */ jsx20(Route, { path: "/forgot", element: /* @__PURE__ */ jsx20(Forgot, {}) }),
2221
+ /* @__PURE__ */ jsx20(Route, { path: "/reset", element: /* @__PURE__ */ jsx20(Reset, {}) }),
2222
+ EmailVerify ? /* @__PURE__ */ jsx20(Route, { path: "/verify", element: /* @__PURE__ */ jsx20(EmailVerify, {}) }) : null,
2223
+ /* @__PURE__ */ jsx20(Route, { path: "*", element: /* @__PURE__ */ jsx20(Navigate, { to: "/", replace: true }) })
2120
2224
  ] });
2121
2225
  }
2122
- return /* @__PURE__ */ jsx19(Fragment6, { children });
2226
+ return /* @__PURE__ */ jsx20(Fragment6, { children });
2123
2227
  }
2124
2228
  function FallbackPaywall() {
2125
2229
  return null;
2126
2230
  }
2127
2231
 
2128
2232
  // src/hooks/usePush.ts
2129
- import { useCallback as useCallback4, useEffect as useEffect7, useState as useState6 } from "react";
2130
- import { useHook as useHook6 } from "@hook-sdk/sdk";
2233
+ import { useCallback as useCallback4, useEffect as useEffect8, useState as useState6 } from "react";
2234
+ import { useHook as useHook7 } from "@hook-sdk/sdk";
2131
2235
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2132
2236
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2133
2237
  function detectIosNeedsInstall() {
@@ -2171,9 +2275,9 @@ function deriveState(push) {
2171
2275
  return { kind: "prompt" };
2172
2276
  }
2173
2277
  function usePush() {
2174
- const { push } = useHook6();
2278
+ const { push } = useHook7();
2175
2279
  const [state, setState] = useState6(() => deriveState(push));
2176
- useEffect7(() => {
2280
+ useEffect8(() => {
2177
2281
  setState(deriveState(push));
2178
2282
  }, [push]);
2179
2283
  const subscribe = useCallback4(async () => {
@@ -2210,7 +2314,7 @@ function usePush() {
2210
2314
  }
2211
2315
 
2212
2316
  // src/components/PushPrompt.tsx
2213
- import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
2317
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2214
2318
  function platformRecoveryCopy(texts) {
2215
2319
  if (typeof navigator === "undefined") return null;
2216
2320
  const ua = navigator.userAgent || "";
@@ -2234,27 +2338,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2234
2338
  if (state.kind === "subscribed" || state.kind === "dismissed") return null;
2235
2339
  if (state.kind === "ios_needs_install") {
2236
2340
  return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2237
- /* @__PURE__ */ jsx20("h3", { children: texts.iosInstallTitle }),
2238
- /* @__PURE__ */ jsx20("p", { children: texts.iosInstallBody }),
2239
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx20("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2341
+ /* @__PURE__ */ jsx21("h3", { children: texts.iosInstallTitle }),
2342
+ /* @__PURE__ */ jsx21("p", { children: texts.iosInstallBody }),
2343
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx21("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2240
2344
  ] });
2241
2345
  }
2242
2346
  if (state.kind === "denied") {
2243
2347
  const recovery = platformRecoveryCopy(texts);
2244
2348
  return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2245
- /* @__PURE__ */ jsx20("h3", { children: texts.deniedTitle }),
2246
- /* @__PURE__ */ jsx20("p", { children: texts.deniedBody }),
2247
- recovery && /* @__PURE__ */ jsx20("p", { "data-testid": "denied-recovery", children: recovery })
2349
+ /* @__PURE__ */ jsx21("h3", { children: texts.deniedTitle }),
2350
+ /* @__PURE__ */ jsx21("p", { children: texts.deniedBody }),
2351
+ recovery && /* @__PURE__ */ jsx21("p", { "data-testid": "denied-recovery", children: recovery })
2248
2352
  ] });
2249
2353
  }
2250
2354
  if (state.kind === "unsupported") {
2251
- return /* @__PURE__ */ jsx20("div", { className, role: "region", children: /* @__PURE__ */ jsx20("p", { children: texts.unsupportedBody }) });
2355
+ return /* @__PURE__ */ jsx21("div", { className, role: "region", children: /* @__PURE__ */ jsx21("p", { children: texts.unsupportedBody }) });
2252
2356
  }
2253
2357
  if (state.kind === "error") {
2254
- return /* @__PURE__ */ jsx20("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx20("p", { children: state.message }) });
2358
+ return /* @__PURE__ */ jsx21("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx21("p", { children: state.message }) });
2255
2359
  }
2256
2360
  return /* @__PURE__ */ jsxs15("div", { className, role: "region", children: [
2257
- /* @__PURE__ */ jsx20(
2361
+ /* @__PURE__ */ jsx21(
2258
2362
  "button",
2259
2363
  {
2260
2364
  type: "button",
@@ -2268,29 +2372,29 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2268
2372
  children: texts.cta
2269
2373
  }
2270
2374
  ),
2271
- onDeclined && /* @__PURE__ */ jsx20("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2375
+ onDeclined && /* @__PURE__ */ jsx21("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2272
2376
  ] });
2273
2377
  }
2274
2378
 
2275
2379
  // src/defaults/LoadingState.tsx
2276
- import { jsx as jsx21 } from "react/jsx-runtime";
2380
+ import { jsx as jsx22 } from "react/jsx-runtime";
2277
2381
  function LoadingState({ message }) {
2278
- return /* @__PURE__ */ jsx21("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx21("span", { children: message ?? "Carregando..." }) });
2382
+ return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx22("span", { children: message ?? "Carregando..." }) });
2279
2383
  }
2280
2384
 
2281
2385
  // src/defaults/EmptyState.tsx
2282
- import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2386
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
2283
2387
  function EmptyState({ title, description, action }) {
2284
2388
  return /* @__PURE__ */ jsxs16("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2285
- /* @__PURE__ */ jsx22("h2", { style: { marginBottom: 8 }, children: title }),
2286
- description && /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7 }, children: description }),
2287
- action && /* @__PURE__ */ jsx22("div", { style: { marginTop: 16 }, children: action })
2389
+ /* @__PURE__ */ jsx23("h2", { style: { marginBottom: 8 }, children: title }),
2390
+ description && /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: description }),
2391
+ action && /* @__PURE__ */ jsx23("div", { style: { marginTop: 16 }, children: action })
2288
2392
  ] });
2289
2393
  }
2290
2394
 
2291
2395
  // src/hooks/useLoginForm.ts
2292
2396
  import { useCallback as useCallback5, useMemo as useMemo4, useState as useState7 } from "react";
2293
- import { useHook as useHook7 } from "@hook-sdk/sdk";
2397
+ import { useHook as useHook8 } from "@hook-sdk/sdk";
2294
2398
 
2295
2399
  // src/errors.ts
2296
2400
  import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
@@ -2325,7 +2429,7 @@ function mapSdkError(err) {
2325
2429
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2326
2430
  var MIN_PASSWORD = 8;
2327
2431
  function useLoginForm() {
2328
- const { auth } = useHook7();
2432
+ const { auth } = useHook8();
2329
2433
  const [email, setEmail] = useState7("");
2330
2434
  const [password, setPassword] = useState7("");
2331
2435
  const [submitting, setSubmitting] = useState7(false);
@@ -2381,11 +2485,11 @@ function useLoginForm() {
2381
2485
 
2382
2486
  // src/hooks/useSignupForm.ts
2383
2487
  import { useCallback as useCallback6, useMemo as useMemo5, useState as useState8 } from "react";
2384
- import { useHook as useHook8 } from "@hook-sdk/sdk";
2488
+ import { useHook as useHook9 } from "@hook-sdk/sdk";
2385
2489
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2386
2490
  var MIN_PASSWORD2 = 8;
2387
2491
  function useSignupForm() {
2388
- const { auth } = useHook8();
2492
+ const { auth } = useHook9();
2389
2493
  const [name, setName] = useState8("");
2390
2494
  const [email, setEmail] = useState8("");
2391
2495
  const [password, setPassword] = useState8("");
@@ -2453,10 +2557,10 @@ function useSignupForm() {
2453
2557
 
2454
2558
  // src/hooks/useForgotForm.ts
2455
2559
  import { useCallback as useCallback7, useMemo as useMemo6, useState as useState9 } from "react";
2456
- import { useHook as useHook9 } from "@hook-sdk/sdk";
2560
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
2457
2561
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2458
2562
  function useForgotForm() {
2459
- const { auth } = useHook9();
2563
+ const { auth } = useHook10();
2460
2564
  const [email, setEmail] = useState9("");
2461
2565
  const [submitting, setSubmitting] = useState9(false);
2462
2566
  const [sent, setSent] = useState9(false);
@@ -2501,11 +2605,11 @@ function useForgotForm() {
2501
2605
  }
2502
2606
 
2503
2607
  // src/hooks/useResetForm.ts
2504
- import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo7, useState as useState10 } from "react";
2505
- import { useHook as useHook10 } from "@hook-sdk/sdk";
2608
+ import { useCallback as useCallback8, useEffect as useEffect9, useMemo as useMemo7, useState as useState10 } from "react";
2609
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
2506
2610
  var MIN_PASSWORD3 = 12;
2507
2611
  function useResetForm() {
2508
- const { auth } = useHook10();
2612
+ const { auth } = useHook11();
2509
2613
  const [token, setToken] = useState10(null);
2510
2614
  const [password, setPassword] = useState10("");
2511
2615
  const [confirm, setConfirm] = useState10("");
@@ -2515,7 +2619,7 @@ function useResetForm() {
2515
2619
  const [touchedPassword, setTouchedPassword] = useState10(false);
2516
2620
  const [touchedConfirm, setTouchedConfirm] = useState10(false);
2517
2621
  const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
2518
- useEffect8(() => {
2622
+ useEffect9(() => {
2519
2623
  if (typeof window === "undefined") return;
2520
2624
  const params = new URLSearchParams(window.location.search);
2521
2625
  const t = params.get("token");
@@ -2574,9 +2678,9 @@ function useResetForm() {
2574
2678
  }
2575
2679
 
2576
2680
  // src/hooks/usePlan.ts
2577
- import { useHook as useHook11 } from "@hook-sdk/sdk";
2681
+ import { useHook as useHook12 } from "@hook-sdk/sdk";
2578
2682
  function usePlan() {
2579
- const { plan } = useHook11();
2683
+ const { plan } = useHook12();
2580
2684
  return plan;
2581
2685
  }
2582
2686
 
@@ -2609,12 +2713,12 @@ function discountPercent(anchorCents, realCents) {
2609
2713
  }
2610
2714
 
2611
2715
  // src/hooks/useAuthPrimitives.ts
2612
- import { useEffect as useEffect9 } from "react";
2613
- import { useHook as useHook12 } from "@hook-sdk/sdk";
2716
+ import { useEffect as useEffect10 } from "react";
2717
+ import { useHook as useHook13 } from "@hook-sdk/sdk";
2614
2718
  var warned = false;
2615
2719
  function useAuthPrimitives() {
2616
- const { auth } = useHook12();
2617
- useEffect9(() => {
2720
+ const { auth } = useHook13();
2721
+ useEffect10(() => {
2618
2722
  if (!warned && process.env.NODE_ENV !== "production") {
2619
2723
  warned = true;
2620
2724
  console.warn(
@@ -2636,9 +2740,9 @@ function useAuthPrimitives() {
2636
2740
  }
2637
2741
 
2638
2742
  // src/hooks/useAuth.ts
2639
- import { useHook as useHook13 } from "@hook-sdk/sdk";
2743
+ import { useHook as useHook14 } from "@hook-sdk/sdk";
2640
2744
  function useAuth() {
2641
- const { user, authStatus, auth } = useHook13();
2745
+ const { user, authStatus, auth } = useHook14();
2642
2746
  return {
2643
2747
  user,
2644
2748
  authStatus,
@@ -2647,19 +2751,19 @@ function useAuth() {
2647
2751
  }
2648
2752
 
2649
2753
  // src/hooks/useSubscription.ts
2650
- import { useHook as useHook14 } from "@hook-sdk/sdk";
2754
+ import { useHook as useHook15 } from "@hook-sdk/sdk";
2651
2755
  function useSubscription() {
2652
- const { subscription } = useHook14();
2756
+ const { subscription } = useHook15();
2653
2757
  return {
2654
2758
  status: subscription.status()
2655
2759
  };
2656
2760
  }
2657
2761
 
2658
2762
  // src/hooks/useReminders.ts
2659
- import { useCallback as useCallback9, useEffect as useEffect10, useState as useState11 } from "react";
2660
- import { useHook as useHook15 } from "@hook-sdk/sdk";
2763
+ import { useCallback as useCallback9, useEffect as useEffect11, useState as useState11 } from "react";
2764
+ import { useHook as useHook16 } from "@hook-sdk/sdk";
2661
2765
  function useReminders() {
2662
- const { push } = useHook15();
2766
+ const { push } = useHook16();
2663
2767
  const r = push.reminders;
2664
2768
  const [reminders, setReminders] = useState11([]);
2665
2769
  const [loading, setLoading] = useState11(true);
@@ -2672,7 +2776,7 @@ function useReminders() {
2672
2776
  setLoading(false);
2673
2777
  }
2674
2778
  }, [r]);
2675
- useEffect10(() => {
2779
+ useEffect11(() => {
2676
2780
  void reload();
2677
2781
  }, [reload]);
2678
2782
  const setReminder = useCallback9(async (input) => {
@@ -2711,20 +2815,20 @@ function useToast() {
2711
2815
 
2712
2816
  // src/RouteBoundary.tsx
2713
2817
  import { Routes as Routes2, Route as Route2 } from "react-router-dom";
2714
- import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2818
+ import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
2715
2819
  function RouteBoundary({ children }) {
2716
2820
  return /* @__PURE__ */ jsxs17(Routes2, { children: [
2717
2821
  children,
2718
- /* @__PURE__ */ jsx23(Route2, { path: "*", element: /* @__PURE__ */ jsx23(DefaultNotFound, {}) })
2822
+ /* @__PURE__ */ jsx24(Route2, { path: "*", element: /* @__PURE__ */ jsx24(DefaultNotFound, {}) })
2719
2823
  ] });
2720
2824
  }
2721
2825
  function DefaultNotFound() {
2722
- return /* @__PURE__ */ jsx23("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
2826
+ return /* @__PURE__ */ jsx24("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
2723
2827
  }
2724
2828
 
2725
2829
  // src/PreAuthShell.tsx
2726
2830
  import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
2727
- import { jsx as jsx24 } from "react/jsx-runtime";
2831
+ import { jsx as jsx25 } from "react/jsx-runtime";
2728
2832
  function PreAuthShell({
2729
2833
  basename,
2730
2834
  testRouter,
@@ -2732,14 +2836,14 @@ function PreAuthShell({
2732
2836
  children
2733
2837
  }) {
2734
2838
  if (testRouter === "memory") {
2735
- return /* @__PURE__ */ jsx24(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx24(Routes3, { children }) });
2839
+ return /* @__PURE__ */ jsx25(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx25(Routes3, { children }) });
2736
2840
  }
2737
- return /* @__PURE__ */ jsx24(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx24(Routes3, { children }) });
2841
+ return /* @__PURE__ */ jsx25(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx25(Routes3, { children }) });
2738
2842
  }
2739
2843
 
2740
2844
  // src/OnboardingFlow.tsx
2741
- import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef4 } from "react";
2742
- import { usePersistedState } from "@hook-sdk/sdk";
2845
+ import { useCallback as useCallback11, useEffect as useEffect12, useMemo as useMemo8, useRef as useRef5 } from "react";
2846
+ import { usePersistedState, useHook as useHook17 } from "@hook-sdk/sdk";
2743
2847
 
2744
2848
  // src/hooks/useOnboardingStep.ts
2745
2849
  import { createContext as createContext3, useContext as useContext4 } from "react";
@@ -2755,7 +2859,7 @@ function useOnboardingStep() {
2755
2859
  }
2756
2860
 
2757
2861
  // src/OnboardingFlow.tsx
2758
- import { jsx as jsx25 } from "react/jsx-runtime";
2862
+ import { jsx as jsx26 } from "react/jsx-runtime";
2759
2863
  var isFilled = (v) => v != null && v !== "";
2760
2864
  var CURRENT_STEP_FIELD = "currentStep";
2761
2865
  function readPersistedStepIdx(draft) {
@@ -2769,7 +2873,7 @@ function OnboardingFlow({
2769
2873
  persistKey
2770
2874
  }) {
2771
2875
  const [draft, setDraft, status] = usePersistedState(persistKey, {});
2772
- const draftRef = useRef4(draft);
2876
+ const draftRef = useRef5(draft);
2773
2877
  draftRef.current = draft;
2774
2878
  const idx = readPersistedStepIdx(draft);
2775
2879
  const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
@@ -2791,6 +2895,18 @@ function OnboardingFlow({
2791
2895
  [setDraft]
2792
2896
  );
2793
2897
  const step = steps[clampedIdx];
2898
+ const hookCtx = useHook17();
2899
+ const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
2900
+ useEffect12(() => {
2901
+ if (status.loading) return;
2902
+ if (!step) return;
2903
+ if (!track2) return;
2904
+ track2("onboarding_step_viewed", {
2905
+ step: step.id,
2906
+ step_index: clampedIdx,
2907
+ total_steps: steps.length
2908
+ });
2909
+ }, [step?.id, clampedIdx, steps.length, status.loading, track2]);
2794
2910
  const valid = useMemo8(
2795
2911
  () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
2796
2912
  [draft, step]
@@ -2833,7 +2949,7 @@ function OnboardingFlow({
2833
2949
  `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
2834
2950
  );
2835
2951
  }
2836
- return /* @__PURE__ */ jsx25(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx25(Screen, {}) });
2952
+ return /* @__PURE__ */ jsx26(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx26(Screen, {}) });
2837
2953
  }
2838
2954
 
2839
2955
  // src/hooks/useFeature.ts