@hook-sdk/template 0.2.0 → 0.4.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.cjs CHANGED
@@ -28,11 +28,21 @@ __export(index_exports, {
28
28
  DefaultSignupScreen: () => DefaultSignupScreen,
29
29
  EmptyState: () => EmptyState,
30
30
  ErrorBoundary: () => ErrorBoundary,
31
+ InstallGate: () => InstallGate,
32
+ InstallSplash: () => InstallSplash,
31
33
  LoadingState: () => LoadingState,
32
34
  PushPrompt: () => PushPrompt2,
35
+ detectAndroidBrowser: () => detectAndroidBrowser,
36
+ detectIOSBrowser: () => detectIOSBrowser,
37
+ detectInAppApp: () => detectInAppApp,
38
+ detectPlatform: () => detectPlatform,
39
+ detectStandalone: () => detectStandalone,
40
+ shouldBlockInstall: () => shouldBlockInstall,
41
+ shouldShowPermanentOption: () => shouldShowPermanentOption,
33
42
  useAuth: () => useAuth,
34
43
  useAuthPrimitives: () => useAuthPrimitives,
35
44
  useForgotForm: () => useForgotForm,
45
+ useInstallPrompt: () => useInstallPrompt,
36
46
  useLoginForm: () => useLoginForm,
37
47
  usePaywallState: () => usePaywallState,
38
48
  usePush: () => usePush,
@@ -45,7 +55,7 @@ __export(index_exports, {
45
55
  module.exports = __toCommonJS(index_exports);
46
56
 
47
57
  // src/AppRoot.tsx
48
- var import_react11 = require("react");
58
+ var import_react16 = require("react");
49
59
  var import_sdk9 = require("@hook-sdk/sdk");
50
60
 
51
61
  // src/internal/TemplateConfigContext.tsx
@@ -210,10 +220,1226 @@ function PushPrompt() {
210
220
  return null;
211
221
  }
212
222
 
213
- // src/defaults/ErrorBoundary.tsx
223
+ // src/components/InstallGate/InstallGate.tsx
224
+ var import_react8 = require("react");
225
+
226
+ // src/hooks/useInstallPrompt.ts
214
227
  var import_react5 = require("react");
228
+ var IOS_RE = /iPad|iPhone|iPod/;
229
+ var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
230
+ var ANDROID_RE = /Android/;
231
+ var IN_APP_RE = /Instagram|FBAN|FBAV|BytedanceWebview|TikTok|Line\/|Twitter|Snapchat|Pinterest|LinkedIn|WhatsApp|MicroMessenger|Telegram/i;
232
+ var PERMANENT_DISMISS_REPROMPT_DAYS = 14;
233
+ var SESSION_SKIPS_BEFORE_PERMANENT_OPTION = 2;
234
+ var storageKey = {
235
+ sessionSkip: (slug) => `install:${slug}:session-skip`,
236
+ dismissedAt: (slug) => `install:${slug}:dismissed-at`,
237
+ installedAt: (slug) => `install:${slug}:installed-at`,
238
+ skipCount: (slug) => `install:${slug}:skip-count`
239
+ };
240
+ function detectPlatform(ua) {
241
+ if (IN_APP_RE.test(ua)) return "in-app";
242
+ const isIOS = IOS_RE.test(ua);
243
+ if (isIOS) {
244
+ const isSafari = /Safari/.test(ua) && !IOS_NON_SAFARI_RE.test(ua);
245
+ return isSafari ? "ios-safari" : "ios-other";
246
+ }
247
+ if (ANDROID_RE.test(ua)) return "android";
248
+ return "desktop";
249
+ }
250
+ function detectIOSBrowser(ua) {
251
+ if (!IOS_RE.test(ua)) return null;
252
+ if (/CriOS/.test(ua)) return "chrome";
253
+ if (/FxiOS/.test(ua)) return "firefox";
254
+ if (/EdgiOS/.test(ua)) return "edge";
255
+ if (/Safari/.test(ua)) return "safari";
256
+ return "other";
257
+ }
258
+ function detectAndroidBrowser(ua) {
259
+ if (!ANDROID_RE.test(ua)) return null;
260
+ if (/EdgA/.test(ua)) return "edge";
261
+ if (/OPR|OPT/.test(ua)) return "opera";
262
+ if (/SamsungBrowser/.test(ua)) return "samsung";
263
+ if (/Firefox/.test(ua)) return "firefox";
264
+ if (/Chrome/.test(ua)) return "chrome";
265
+ return "other";
266
+ }
267
+ function detectInAppApp(ua) {
268
+ if (!IN_APP_RE.test(ua)) return null;
269
+ if (/Instagram/i.test(ua)) return "instagram";
270
+ if (/FBAN|FBAV/.test(ua)) return "facebook";
271
+ if (/BytedanceWebview|TikTok/i.test(ua)) return "tiktok";
272
+ if (/WhatsApp/i.test(ua)) return "whatsapp";
273
+ if (/Twitter/i.test(ua)) return "twitter";
274
+ if (/LinkedIn/i.test(ua)) return "linkedin";
275
+ if (/Telegram/i.test(ua)) return "telegram";
276
+ if (/Line\//i.test(ua)) return "line";
277
+ if (/Snapchat/i.test(ua)) return "snapchat";
278
+ if (/Pinterest/i.test(ua)) return "pinterest";
279
+ if (/MicroMessenger/i.test(ua)) return "wechat";
280
+ return "other";
281
+ }
282
+ function detectStandalone() {
283
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
284
+ return { installed: false, source: null };
285
+ }
286
+ const mm = window.matchMedia?.("(display-mode: standalone)");
287
+ if (mm?.matches) return { installed: true, source: "match_media" };
288
+ const fs = window.matchMedia?.("(display-mode: fullscreen)");
289
+ if (fs?.matches) return { installed: true, source: "match_media" };
290
+ if (navigator.standalone === true) return { installed: true, source: "navigator_standalone" };
291
+ return { installed: false, source: null };
292
+ }
293
+ function track(event, props) {
294
+ if (typeof window === "undefined") return;
295
+ window.posthog?.capture?.(event, props);
296
+ }
297
+ function pickVariant(state) {
298
+ if (state.isInstalled) return "none";
299
+ switch (state.platform) {
300
+ case "android":
301
+ return state.isInstallable ? "android-native" : "android-manual";
302
+ case "ios-safari":
303
+ return "ios-safari";
304
+ case "ios-other":
305
+ return "ios-other";
306
+ case "in-app":
307
+ return "in-app";
308
+ case "desktop":
309
+ return state.isInstallable ? "desktop" : "none";
310
+ default:
311
+ return "none";
312
+ }
313
+ }
314
+ function safeStorage() {
315
+ if (typeof localStorage === "undefined") return null;
316
+ try {
317
+ localStorage.setItem("__install_probe", "1");
318
+ localStorage.removeItem("__install_probe");
319
+ return localStorage;
320
+ } catch {
321
+ return null;
322
+ }
323
+ }
324
+ function readPermanentDismiss(slug) {
325
+ const storage = safeStorage();
326
+ if (!storage) return { dismissed: false, dismissedAt: null };
327
+ const raw = storage.getItem(storageKey.dismissedAt(slug));
328
+ if (!raw) return { dismissed: false, dismissedAt: null };
329
+ const parsed = Date.parse(raw);
330
+ if (Number.isNaN(parsed)) return { dismissed: false, dismissedAt: null };
331
+ const daysAgo = (Date.now() - parsed) / (1e3 * 60 * 60 * 24);
332
+ return { dismissed: daysAgo < PERMANENT_DISMISS_REPROMPT_DAYS, dismissedAt: raw };
333
+ }
334
+ function readInstalledMarker(slug) {
335
+ const storage = safeStorage();
336
+ if (!storage) return false;
337
+ return storage.getItem(storageKey.installedAt(slug)) !== null;
338
+ }
339
+ function readSkipCount(slug) {
340
+ const storage = safeStorage();
341
+ if (!storage) return 0;
342
+ const raw = storage.getItem(storageKey.skipCount(slug));
343
+ const n = raw ? Number.parseInt(raw, 10) : 0;
344
+ return Number.isFinite(n) && n >= 0 ? n : 0;
345
+ }
346
+ function readSessionSkip(slug) {
347
+ if (typeof sessionStorage === "undefined") return false;
348
+ try {
349
+ return sessionStorage.getItem(storageKey.sessionSkip(slug)) === "true";
350
+ } catch {
351
+ return false;
352
+ }
353
+ }
354
+ function useInstallPrompt(slug) {
355
+ const ua = typeof navigator !== "undefined" && typeof navigator.userAgent === "string" ? navigator.userAgent : "";
356
+ const platform = detectPlatform(ua);
357
+ const iosBrowser = detectIOSBrowser(ua);
358
+ const androidBrowser = detectAndroidBrowser(ua);
359
+ const inAppApp = detectInAppApp(ua);
360
+ const [isInstallable, setIsInstallable] = (0, import_react5.useState)(() => {
361
+ if (typeof window === "undefined") return false;
362
+ return window.__pwaInstallPrompt != null;
363
+ });
364
+ const [isInstalled, setIsInstalled] = (0, import_react5.useState)(() => {
365
+ const { installed } = detectStandalone();
366
+ return installed || readInstalledMarker(slug);
367
+ });
368
+ const [isDismissedSession, setIsDismissedSession] = (0, import_react5.useState)(() => readSessionSkip(slug));
369
+ const [isDismissedPermanent, setIsDismissedPermanent] = (0, import_react5.useState)(() => readPermanentDismiss(slug).dismissed);
370
+ const [skipCount, setSkipCount] = (0, import_react5.useState)(() => readSkipCount(slug));
371
+ (0, import_react5.useEffect)(() => {
372
+ if (typeof window === "undefined") return;
373
+ if (window.__pwaInstallPrompt) {
374
+ setIsInstallable(true);
375
+ }
376
+ const onPrompt = (e) => {
377
+ e.preventDefault();
378
+ window.__pwaInstallPrompt = e;
379
+ setIsInstallable(true);
380
+ };
381
+ const onInstalled = () => {
382
+ setIsInstalled(true);
383
+ setIsInstallable(false);
384
+ window.__pwaInstallPrompt = null;
385
+ const storage = safeStorage();
386
+ if (storage) storage.setItem(storageKey.installedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
387
+ };
388
+ window.addEventListener("beforeinstallprompt", onPrompt);
389
+ window.addEventListener("appinstalled", onInstalled);
390
+ return () => {
391
+ window.removeEventListener("beforeinstallprompt", onPrompt);
392
+ window.removeEventListener("appinstalled", onInstalled);
393
+ };
394
+ }, [slug]);
395
+ (0, import_react5.useEffect)(() => {
396
+ if (typeof window === "undefined") return;
397
+ const mq = window.matchMedia?.("(display-mode: standalone)");
398
+ if (!mq) return;
399
+ const handler = (e) => {
400
+ if (e.matches) {
401
+ setIsInstalled(true);
402
+ track("pwa_install_standalone_detected", { slug, source: "match_media" });
403
+ }
404
+ };
405
+ mq.addEventListener?.("change", handler);
406
+ return () => mq.removeEventListener?.("change", handler);
407
+ }, [slug]);
408
+ const variant = pickVariant({
409
+ platform,
410
+ iosBrowser,
411
+ androidBrowser,
412
+ inAppApp,
413
+ isInstallable,
414
+ isInstalled,
415
+ isDismissedSession,
416
+ isDismissedPermanent,
417
+ skipCount
418
+ });
419
+ const promptInstall = (0, import_react5.useCallback)(async () => {
420
+ if (typeof window === "undefined") return false;
421
+ const prompt = window.__pwaInstallPrompt;
422
+ if (!prompt) return false;
423
+ track("pwa_install_prompt_clicked", { slug });
424
+ try {
425
+ await prompt.prompt();
426
+ const { outcome } = await prompt.userChoice;
427
+ track("pwa_install_prompt_outcome", { slug, outcome });
428
+ if (outcome === "accepted") {
429
+ setIsInstalled(true);
430
+ setIsInstallable(false);
431
+ window.__pwaInstallPrompt = null;
432
+ const storage = safeStorage();
433
+ if (storage) storage.setItem(storageKey.installedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
434
+ return true;
435
+ }
436
+ return false;
437
+ } catch {
438
+ return false;
439
+ }
440
+ }, [slug]);
441
+ const dismissSession = (0, import_react5.useCallback)(() => {
442
+ if (typeof sessionStorage !== "undefined") {
443
+ try {
444
+ sessionStorage.setItem(storageKey.sessionSkip(slug), "true");
445
+ } catch {
446
+ }
447
+ }
448
+ const storage = safeStorage();
449
+ const newCount = skipCount + 1;
450
+ if (storage) storage.setItem(storageKey.skipCount(slug), String(newCount));
451
+ setSkipCount(newCount);
452
+ setIsDismissedSession(true);
453
+ track("pwa_install_session_skip", { slug, platform, skip_count: newCount });
454
+ }, [slug, skipCount, platform]);
455
+ const dismissPermanent = (0, import_react5.useCallback)(() => {
456
+ const storage = safeStorage();
457
+ if (storage) storage.setItem(storageKey.dismissedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
458
+ setIsDismissedPermanent(true);
459
+ track("pwa_install_permanent_dismiss", { slug, platform, prior_skip_count: skipCount });
460
+ }, [slug, platform, skipCount]);
461
+ const redirectToSafari = (0, import_react5.useCallback)(() => {
462
+ if (typeof location === "undefined") return;
463
+ track("pwa_install_redirect_to_safari", { slug });
464
+ const url = `safari-https://${location.host}${location.pathname}${location.search}${location.hash}`;
465
+ try {
466
+ window.location.href = url;
467
+ } catch {
468
+ }
469
+ }, [slug]);
470
+ const copyLink = (0, import_react5.useCallback)(async () => {
471
+ if (typeof navigator === "undefined" || typeof location === "undefined") return;
472
+ try {
473
+ await navigator.clipboard?.writeText?.(location.href);
474
+ } catch {
475
+ }
476
+ }, []);
477
+ const reset = (0, import_react5.useCallback)(() => {
478
+ const storage = safeStorage();
479
+ if (storage) {
480
+ storage.removeItem(storageKey.dismissedAt(slug));
481
+ storage.removeItem(storageKey.installedAt(slug));
482
+ storage.removeItem(storageKey.skipCount(slug));
483
+ }
484
+ if (typeof sessionStorage !== "undefined") {
485
+ try {
486
+ sessionStorage.removeItem(storageKey.sessionSkip(slug));
487
+ } catch {
488
+ }
489
+ }
490
+ setIsDismissedSession(false);
491
+ setIsDismissedPermanent(false);
492
+ setSkipCount(0);
493
+ }, [slug]);
494
+ return {
495
+ platform,
496
+ iosBrowser,
497
+ androidBrowser,
498
+ inAppApp,
499
+ isInstallable,
500
+ isInstalled,
501
+ isDismissedSession,
502
+ isDismissedPermanent,
503
+ skipCount,
504
+ variant,
505
+ promptInstall,
506
+ dismissSession,
507
+ dismissPermanent,
508
+ redirectToSafari,
509
+ copyLink,
510
+ reset
511
+ };
512
+ }
513
+ function shouldBlockInstall(state, now = Date.now()) {
514
+ if (state.isInstalled) return false;
515
+ if (state.variant === "none") return false;
516
+ if (state.isDismissedSession) return false;
517
+ if (state.isDismissedPermanent) {
518
+ void now;
519
+ return false;
520
+ }
521
+ if (state.platform === "desktop" && !state.isInstallable) return false;
522
+ if (state.platform === "unknown") return false;
523
+ return true;
524
+ }
525
+ function shouldShowPermanentOption(state) {
526
+ return state.skipCount >= SESSION_SKIPS_BEFORE_PERMANENT_OPTION;
527
+ }
528
+
529
+ // src/components/InstallGate/copy.ts
530
+ var INSTALL_COPY = {
531
+ android: {
532
+ native: {
533
+ title: "Instale no seu celular",
534
+ subtitle: "Acesso r\xE1pido, sem precisar do navegador",
535
+ cta: "Baixar",
536
+ skip: "Continuar no navegador",
537
+ skipPermanent: "N\xE3o me pergunte mais"
538
+ },
539
+ manual: {
540
+ title: "Instale em 2 toques",
541
+ subtitle: 'Toque no menu do navegador e escolha "Instalar aplicativo"',
542
+ step1: "Toque no menu do navegador",
543
+ step2: 'Escolha "Instalar aplicativo"',
544
+ cta: "Entendi",
545
+ skip: "Continuar no navegador",
546
+ skipPermanent: "N\xE3o me pergunte mais"
547
+ }
548
+ },
549
+ iosSafari: {
550
+ title: "Adicione \xE0 sua Tela de In\xEDcio",
551
+ subtitle: "Siga os 3 passos",
552
+ step1: {
553
+ title: "Toque em Compartilhar",
554
+ subtitle: "Na barra inferior do Safari"
555
+ },
556
+ step2: {
557
+ title: 'Role e toque em "Adicionar \xE0 Tela de In\xEDcio"',
558
+ iconLabel: "Adicionar \xE0 Tela de In\xEDcio"
559
+ },
560
+ step3: {
561
+ title: 'Toque em "Adicionar" pra confirmar',
562
+ buttonLabel: "Adicionar"
563
+ },
564
+ skip: "Continuar no Safari",
565
+ skipPermanent: "N\xE3o me pergunte mais"
566
+ },
567
+ iosOther: {
568
+ title: "Abra no Safari pra instalar",
569
+ subtitle: "No Chrome/Firefox/Edge do iPhone n\xE3o d\xE1 pra instalar PWA. Abra o link no Safari.",
570
+ ctaPrimary: "Abrir no Safari",
571
+ ctaSecondary: "Copiar link",
572
+ copiedToast: "Link copiado. Cole no Safari.",
573
+ skip: "Continuar mesmo assim",
574
+ skipPermanent: "N\xE3o me pergunte mais"
575
+ },
576
+ inApp: {
577
+ instagram: {
578
+ title: "Pra instalar, abra fora do Instagram",
579
+ step1: "Toque em \u22EF (canto superior direito)",
580
+ step2: 'Escolha "Abrir no navegador externo"'
581
+ },
582
+ facebook: {
583
+ title: "Pra instalar, abra fora do Facebook",
584
+ step1: "Toque em \u22EE",
585
+ step2: 'Escolha "Abrir no navegador"'
586
+ },
587
+ tiktok: {
588
+ title: "Pra instalar, abra fora do TikTok",
589
+ step1: "Toque em \u22EF",
590
+ step2: 'Escolha "Abrir no Safari" (iOS) ou "Abrir no Chrome" (Android)'
591
+ },
592
+ whatsapp: {
593
+ title: "Pra instalar, abra fora do WhatsApp",
594
+ step1: "Toque longo no link",
595
+ step2: 'Escolha "Abrir no navegador"'
596
+ },
597
+ twitter: {
598
+ title: "Pra instalar, abra fora do Twitter",
599
+ step1: "Toque em \u22EF",
600
+ step2: 'Escolha "Abrir no navegador"'
601
+ },
602
+ linkedin: {
603
+ title: "Pra instalar, abra fora do LinkedIn",
604
+ step1: "Toque em \u22EF",
605
+ step2: 'Escolha "Abrir no navegador"'
606
+ },
607
+ telegram: {
608
+ title: "Pra instalar, abra fora do Telegram",
609
+ step1: "Toque em \u22EE",
610
+ step2: 'Escolha "Abrir no navegador"'
611
+ },
612
+ line: {
613
+ title: "Pra instalar, abra fora do LINE",
614
+ step1: "Toque em \u22EF",
615
+ step2: 'Escolha "Abrir no navegador"'
616
+ },
617
+ snapchat: {
618
+ title: "Pra instalar, abra fora do Snapchat",
619
+ step1: "Mantenha pressionado o link",
620
+ step2: 'Escolha "Abrir no navegador"'
621
+ },
622
+ pinterest: {
623
+ title: "Pra instalar, abra fora do Pinterest",
624
+ step1: "Toque em \u22EF",
625
+ step2: 'Escolha "Abrir no navegador"'
626
+ },
627
+ wechat: {
628
+ title: "Pra instalar, abra fora do WeChat",
629
+ step1: "Toque em \u22EF",
630
+ step2: 'Escolha "Abrir no navegador"'
631
+ },
632
+ other: {
633
+ title: "Abra no navegador do celular",
634
+ step1: "Toque no menu do app atual",
635
+ step2: 'Escolha "Abrir no Chrome" ou "Abrir no Safari"'
636
+ },
637
+ copy: "Copiar link",
638
+ copiedToast: "Link copiado. Cole no Chrome/Safari.",
639
+ skip: "Continuar aqui mesmo",
640
+ skipPermanent: "N\xE3o me pergunte mais"
641
+ },
642
+ desktop: {
643
+ title: "Instale no computador",
644
+ subtitle: "Acesso r\xE1pido",
645
+ cta: "Baixar",
646
+ close: "Fechar"
647
+ }
648
+ };
649
+
650
+ // src/components/InstallGate/InstallSplash.tsx
215
651
  var import_jsx_runtime7 = require("react/jsx-runtime");
216
- var ErrorBoundary = class extends import_react5.Component {
652
+ function InstallSplash({ children, title, subtitle }) {
653
+ const { name, theme } = useTemplateConfig();
654
+ const iconUrl = theme.icon_url || theme.logo_url || null;
655
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
656
+ "div",
657
+ {
658
+ role: "dialog",
659
+ "aria-modal": "true",
660
+ "aria-labelledby": "install-splash-title",
661
+ "aria-describedby": subtitle ? "install-splash-subtitle" : void 0,
662
+ style: overlayStyle,
663
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: cardStyle, children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", justifyContent: "center", marginBottom: 16 }, children: iconUrl ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
665
+ "img",
666
+ {
667
+ src: iconUrl,
668
+ alt: `\xCDcone de ${name}`,
669
+ style: { width: 80, height: 80, borderRadius: 20, objectFit: "cover" }
670
+ }
671
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
672
+ "div",
673
+ {
674
+ style: {
675
+ width: 80,
676
+ height: 80,
677
+ borderRadius: 20,
678
+ background: "var(--hook-color-primary)",
679
+ color: "#fff",
680
+ display: "flex",
681
+ alignItems: "center",
682
+ justifyContent: "center",
683
+ fontSize: 36,
684
+ fontWeight: 700
685
+ },
686
+ children: name.charAt(0).toUpperCase()
687
+ }
688
+ ) }),
689
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { id: "install-splash-title", style: titleStyle, children: title }),
690
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { id: "install-splash-subtitle", style: subtitleStyle, children: subtitle }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { marginTop: 24 }, children }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: footerStyle, children: "por Hook" })
693
+ ] })
694
+ }
695
+ );
696
+ }
697
+ var primaryButtonStyle = {
698
+ width: "100%",
699
+ padding: "14px 20px",
700
+ background: "var(--hook-color-primary)",
701
+ color: "#fff",
702
+ border: "none",
703
+ borderRadius: 999,
704
+ fontSize: 17,
705
+ fontWeight: 600,
706
+ cursor: "pointer",
707
+ marginBottom: 12
708
+ };
709
+ var secondaryButtonStyle = {
710
+ width: "100%",
711
+ padding: "12px 20px",
712
+ background: "transparent",
713
+ color: "var(--hook-color-primary)",
714
+ border: "1px solid var(--hook-color-primary)",
715
+ borderRadius: 999,
716
+ fontSize: 15,
717
+ fontWeight: 500,
718
+ cursor: "pointer",
719
+ marginBottom: 12
720
+ };
721
+ var skipLinkStyle = {
722
+ display: "block",
723
+ width: "100%",
724
+ padding: "10px",
725
+ marginTop: 8,
726
+ background: "transparent",
727
+ color: "#555",
728
+ border: "none",
729
+ fontSize: 14,
730
+ textDecoration: "underline",
731
+ cursor: "pointer",
732
+ textAlign: "center"
733
+ };
734
+ var skipPermanentLinkStyle = {
735
+ ...skipLinkStyle,
736
+ color: "#999",
737
+ fontSize: 13,
738
+ marginTop: 4
739
+ };
740
+ var overlayStyle = {
741
+ position: "fixed",
742
+ inset: 0,
743
+ background: "var(--hook-color-background, #fafafa)",
744
+ zIndex: 1e4,
745
+ display: "flex",
746
+ alignItems: "center",
747
+ justifyContent: "center",
748
+ padding: 20,
749
+ overflow: "auto"
750
+ };
751
+ var cardStyle = {
752
+ width: "100%",
753
+ maxWidth: 420,
754
+ padding: 24,
755
+ textAlign: "center"
756
+ };
757
+ var titleStyle = {
758
+ fontSize: 24,
759
+ fontWeight: 700,
760
+ lineHeight: 1.2,
761
+ margin: "0 0 8px 0",
762
+ color: "#111"
763
+ };
764
+ var subtitleStyle = {
765
+ fontSize: 15,
766
+ lineHeight: 1.4,
767
+ color: "#555",
768
+ margin: 0
769
+ };
770
+ var footerStyle = {
771
+ fontSize: 11,
772
+ color: "#aaa",
773
+ marginTop: 32,
774
+ letterSpacing: 0.5
775
+ };
776
+
777
+ // src/components/InstallGate/icons.tsx
778
+ var import_jsx_runtime8 = require("react/jsx-runtime");
779
+ var defaultSvgProps = (size) => ({
780
+ width: size,
781
+ height: size,
782
+ viewBox: "0 0 24 24",
783
+ fill: "none",
784
+ stroke: "currentColor",
785
+ strokeWidth: 2,
786
+ strokeLinecap: "round",
787
+ strokeLinejoin: "round"
788
+ });
789
+ function ShareIconIOS({ size = 24, style, className }) {
790
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
791
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 2L12 15" }),
792
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M8 6L12 2L16 6" }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M4 11v9a2 2 0 002 2h12a2 2 0 002-2v-9" })
794
+ ] });
795
+ }
796
+ function MenuDotsVerticalIcon({ size = 24, style, className }) {
797
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "5", r: "1.5" }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "19", r: "1.5" })
801
+ ] });
802
+ }
803
+ function MenuDotsHorizontalIcon({ size = 24, style, className }) {
804
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "5", cy: "12", r: "1.5" }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "19", cy: "12", r: "1.5" })
808
+ ] });
809
+ }
810
+ function SquarePlusIcon({ size = 24, style, className }) {
811
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 8v8" }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M8 12h8" })
815
+ ] });
816
+ }
817
+ function DownloadIcon({ size = 24, style, className }) {
818
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
819
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }),
820
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "7 10 12 15 17 10" }),
821
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
822
+ ] });
823
+ }
824
+ function ExternalLinkIcon({ size = 24, style, className }) {
825
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
826
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "15 3 21 3 21 9" }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
829
+ ] });
830
+ }
831
+ function XIcon({ size = 20, style, className }) {
832
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
833
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
835
+ ] });
836
+ }
837
+
838
+ // src/components/InstallGate/variants/AndroidNativeVariant.tsx
839
+ var import_jsx_runtime9 = require("react/jsx-runtime");
840
+ function AndroidNativeVariant({
841
+ state,
842
+ actions
843
+ }) {
844
+ const copy = INSTALL_COPY.android.native;
845
+ const showPermanent = shouldShowPermanentOption(state);
846
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
848
+ "button",
849
+ {
850
+ "data-testid": "install-prompt-cta-android-native",
851
+ type: "button",
852
+ onClick: () => void actions.promptInstall(),
853
+ style: { ...primaryButtonStyle, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8 },
854
+ children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DownloadIcon, { size: 18 }),
856
+ copy.cta
857
+ ]
858
+ }
859
+ ),
860
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
861
+ "button",
862
+ {
863
+ "data-testid": "install-prompt-skip-session",
864
+ type: "button",
865
+ onClick: actions.dismissSession,
866
+ style: skipLinkStyle,
867
+ children: copy.skip
868
+ }
869
+ ),
870
+ showPermanent && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
871
+ "button",
872
+ {
873
+ "data-testid": "install-prompt-skip-permanent",
874
+ type: "button",
875
+ onClick: actions.dismissPermanent,
876
+ style: skipPermanentLinkStyle,
877
+ children: copy.skipPermanent
878
+ }
879
+ )
880
+ ] });
881
+ }
882
+
883
+ // src/components/InstallGate/variants/AndroidManualVariant.tsx
884
+ var import_jsx_runtime10 = require("react/jsx-runtime");
885
+ function AndroidManualVariant({
886
+ state,
887
+ actions
888
+ }) {
889
+ const copy = INSTALL_COPY.android.manual;
890
+ const showPermanent = shouldShowPermanentOption(state);
891
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(InstallSplash, { title: copy.title, children: [
892
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Step, { n: 1, icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MenuDotsVerticalIcon, { size: 20 }), children: copy.step1 }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Step, { n: 2, icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DownloadIcon, { size: 18 }), children: copy.step2 }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
895
+ "button",
896
+ {
897
+ "data-testid": "install-prompt-cta-android-manual",
898
+ type: "button",
899
+ onClick: actions.dismissSession,
900
+ style: primaryButtonStyle,
901
+ children: copy.cta
902
+ }
903
+ ),
904
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
905
+ "button",
906
+ {
907
+ "data-testid": "install-prompt-skip-session",
908
+ type: "button",
909
+ onClick: actions.dismissSession,
910
+ style: skipLinkStyle,
911
+ children: copy.skip
912
+ }
913
+ ),
914
+ showPermanent && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
915
+ "button",
916
+ {
917
+ "data-testid": "install-prompt-skip-permanent",
918
+ type: "button",
919
+ onClick: actions.dismissPermanent,
920
+ style: skipPermanentLinkStyle,
921
+ children: copy.skipPermanent
922
+ }
923
+ )
924
+ ] });
925
+ }
926
+ function Step({ n, icon, children }) {
927
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
928
+ "div",
929
+ {
930
+ style: {
931
+ display: "flex",
932
+ alignItems: "center",
933
+ gap: 12,
934
+ padding: "12px 14px",
935
+ background: "#f5f5f7",
936
+ borderRadius: 12,
937
+ marginBottom: 10,
938
+ textAlign: "left"
939
+ },
940
+ children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
942
+ "div",
943
+ {
944
+ style: {
945
+ width: 28,
946
+ height: 28,
947
+ borderRadius: "50%",
948
+ background: "var(--hook-color-primary)",
949
+ color: "#fff",
950
+ display: "flex",
951
+ alignItems: "center",
952
+ justifyContent: "center",
953
+ fontSize: 14,
954
+ fontWeight: 700,
955
+ flexShrink: 0
956
+ },
957
+ children: n
958
+ }
959
+ ),
960
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { flex: 1, fontSize: 15, color: "#333" }, children }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
962
+ ]
963
+ }
964
+ );
965
+ }
966
+
967
+ // src/components/InstallGate/variants/IOSafariVariant.tsx
968
+ var import_jsx_runtime11 = require("react/jsx-runtime");
969
+ function IOSafariVariant({
970
+ state,
971
+ actions
972
+ }) {
973
+ const copy = INSTALL_COPY.iosSafari;
974
+ const showPermanent = shouldShowPermanentOption(state);
975
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
977
+ Step2,
978
+ {
979
+ n: 1,
980
+ title: copy.step1.title,
981
+ subtitle: copy.step1.subtitle,
982
+ visual: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
983
+ "div",
984
+ {
985
+ style: {
986
+ display: "flex",
987
+ justifyContent: "center",
988
+ alignItems: "center",
989
+ background: "#f5f5f7",
990
+ borderRadius: 12,
991
+ padding: "12px 0",
992
+ marginTop: 8
993
+ },
994
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
995
+ }
996
+ )
997
+ }
998
+ ),
999
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1000
+ Step2,
1001
+ {
1002
+ n: 2,
1003
+ title: copy.step2.title,
1004
+ visual: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1005
+ "div",
1006
+ {
1007
+ style: {
1008
+ display: "flex",
1009
+ alignItems: "center",
1010
+ gap: 10,
1011
+ background: "#f5f5f7",
1012
+ borderRadius: 12,
1013
+ padding: "12px 14px",
1014
+ marginTop: 8
1015
+ },
1016
+ children: [
1017
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1018
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1019
+ ]
1020
+ }
1021
+ )
1022
+ }
1023
+ ),
1024
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1025
+ Step2,
1026
+ {
1027
+ n: 3,
1028
+ title: copy.step3.title,
1029
+ visual: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1030
+ "div",
1031
+ {
1032
+ style: {
1033
+ display: "flex",
1034
+ justifyContent: "flex-end",
1035
+ background: "#f5f5f7",
1036
+ borderRadius: 12,
1037
+ padding: "10px 14px",
1038
+ marginTop: 8
1039
+ },
1040
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1041
+ "span",
1042
+ {
1043
+ style: {
1044
+ color: "var(--hook-color-primary)",
1045
+ fontSize: 15,
1046
+ fontWeight: 600
1047
+ },
1048
+ children: copy.step3.buttonLabel
1049
+ }
1050
+ )
1051
+ }
1052
+ )
1053
+ }
1054
+ ),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1056
+ "button",
1057
+ {
1058
+ "data-testid": "install-prompt-skip-session",
1059
+ type: "button",
1060
+ onClick: actions.dismissSession,
1061
+ style: { ...skipLinkStyle, marginTop: 16 },
1062
+ children: copy.skip
1063
+ }
1064
+ ),
1065
+ showPermanent && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1066
+ "button",
1067
+ {
1068
+ "data-testid": "install-prompt-skip-permanent",
1069
+ type: "button",
1070
+ onClick: actions.dismissPermanent,
1071
+ style: skipPermanentLinkStyle,
1072
+ children: copy.skipPermanent
1073
+ }
1074
+ )
1075
+ ] });
1076
+ }
1077
+ function Step2({
1078
+ n,
1079
+ title,
1080
+ subtitle,
1081
+ visual
1082
+ }) {
1083
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1084
+ "div",
1085
+ {
1086
+ style: {
1087
+ display: "flex",
1088
+ alignItems: "flex-start",
1089
+ gap: 12,
1090
+ marginBottom: 16,
1091
+ textAlign: "left"
1092
+ },
1093
+ children: [
1094
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1095
+ "div",
1096
+ {
1097
+ style: {
1098
+ width: 32,
1099
+ height: 32,
1100
+ borderRadius: 10,
1101
+ background: "var(--hook-color-primary)",
1102
+ color: "#fff",
1103
+ display: "flex",
1104
+ alignItems: "center",
1105
+ justifyContent: "center",
1106
+ fontSize: 15,
1107
+ fontWeight: 700,
1108
+ flexShrink: 0
1109
+ },
1110
+ children: n
1111
+ }
1112
+ ),
1113
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { flex: 1 }, children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
1115
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
1116
+ visual
1117
+ ] })
1118
+ ]
1119
+ }
1120
+ );
1121
+ }
1122
+
1123
+ // src/components/InstallGate/variants/IOSOtherVariant.tsx
1124
+ var import_react6 = require("react");
1125
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1126
+ function IOSOtherVariant({
1127
+ state,
1128
+ actions
1129
+ }) {
1130
+ const copy = INSTALL_COPY.iosOther;
1131
+ const showPermanent = shouldShowPermanentOption(state);
1132
+ const [copied, setCopied] = (0, import_react6.useState)(false);
1133
+ const handleCopy = async () => {
1134
+ await actions.copyLink();
1135
+ setCopied(true);
1136
+ setTimeout(() => setCopied(false), 2e3);
1137
+ };
1138
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1140
+ "button",
1141
+ {
1142
+ "data-testid": "install-prompt-cta-ios-other-primary",
1143
+ type: "button",
1144
+ onClick: actions.redirectToSafari,
1145
+ style: primaryButtonStyle,
1146
+ children: copy.ctaPrimary
1147
+ }
1148
+ ),
1149
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1150
+ "button",
1151
+ {
1152
+ "data-testid": "install-prompt-cta-ios-other-secondary",
1153
+ type: "button",
1154
+ onClick: () => void handleCopy(),
1155
+ style: secondaryButtonStyle,
1156
+ children: copied ? copy.copiedToast : copy.ctaSecondary
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1160
+ "button",
1161
+ {
1162
+ "data-testid": "install-prompt-skip-session",
1163
+ type: "button",
1164
+ onClick: actions.dismissSession,
1165
+ style: skipLinkStyle,
1166
+ children: copy.skip
1167
+ }
1168
+ ),
1169
+ showPermanent && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1170
+ "button",
1171
+ {
1172
+ "data-testid": "install-prompt-skip-permanent",
1173
+ type: "button",
1174
+ onClick: actions.dismissPermanent,
1175
+ style: skipPermanentLinkStyle,
1176
+ children: copy.skipPermanent
1177
+ }
1178
+ )
1179
+ ] });
1180
+ }
1181
+
1182
+ // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1183
+ var import_react7 = require("react");
1184
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1185
+ function InAppBrowserVariant({
1186
+ state,
1187
+ actions
1188
+ }) {
1189
+ const app = state.inAppApp ?? "other";
1190
+ const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1191
+ const copy = INSTALL_COPY.inApp;
1192
+ const showPermanent = shouldShowPermanentOption(state);
1193
+ const [copied, setCopied] = (0, import_react7.useState)(false);
1194
+ const handleCopy = async () => {
1195
+ await actions.copyLink();
1196
+ setCopied(true);
1197
+ setTimeout(() => setCopied(false), 2e3);
1198
+ };
1199
+ const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
1200
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(InstallSplash, { title: appCopy.title, children: [
1201
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Step3, { n: 1, icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DotsIcon, { size: 20 }), children: appCopy.step1 }),
1202
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Step3, { n: 2, icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1204
+ "button",
1205
+ {
1206
+ "data-testid": "install-prompt-cta-inapp-copy",
1207
+ type: "button",
1208
+ onClick: () => void handleCopy(),
1209
+ style: { ...primaryButtonStyle, marginTop: 8 },
1210
+ children: copied ? copy.copiedToast : copy.copy
1211
+ }
1212
+ ),
1213
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1214
+ "button",
1215
+ {
1216
+ "data-testid": "install-prompt-skip-session",
1217
+ type: "button",
1218
+ onClick: actions.dismissSession,
1219
+ style: skipLinkStyle,
1220
+ children: copy.skip
1221
+ }
1222
+ ),
1223
+ showPermanent && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1224
+ "button",
1225
+ {
1226
+ "data-testid": "install-prompt-skip-permanent",
1227
+ type: "button",
1228
+ onClick: actions.dismissPermanent,
1229
+ style: skipPermanentLinkStyle,
1230
+ children: copy.skipPermanent
1231
+ }
1232
+ )
1233
+ ] });
1234
+ }
1235
+ function Step3({
1236
+ n,
1237
+ icon,
1238
+ children
1239
+ }) {
1240
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1241
+ "div",
1242
+ {
1243
+ style: {
1244
+ display: "flex",
1245
+ alignItems: "center",
1246
+ gap: 12,
1247
+ padding: "12px 14px",
1248
+ background: "#f5f5f7",
1249
+ borderRadius: 12,
1250
+ marginBottom: 10,
1251
+ textAlign: "left"
1252
+ },
1253
+ children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1255
+ "div",
1256
+ {
1257
+ style: {
1258
+ width: 28,
1259
+ height: 28,
1260
+ borderRadius: "50%",
1261
+ background: "var(--hook-color-primary)",
1262
+ color: "#fff",
1263
+ display: "flex",
1264
+ alignItems: "center",
1265
+ justifyContent: "center",
1266
+ fontSize: 14,
1267
+ fontWeight: 700,
1268
+ flexShrink: 0
1269
+ },
1270
+ children: n
1271
+ }
1272
+ ),
1273
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
1275
+ ]
1276
+ }
1277
+ );
1278
+ }
1279
+
1280
+ // src/components/InstallGate/variants/DesktopVariant.tsx
1281
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1282
+ function DesktopVariant({
1283
+ state,
1284
+ actions
1285
+ }) {
1286
+ const { name, theme } = useTemplateConfig();
1287
+ const copy = INSTALL_COPY.desktop;
1288
+ const iconUrl = theme.icon_url || theme.logo_url || null;
1289
+ if (!state.isInstallable) return null;
1290
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1291
+ "div",
1292
+ {
1293
+ role: "complementary",
1294
+ "aria-label": copy.title,
1295
+ style: bannerStyle,
1296
+ children: [
1297
+ iconUrl ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1298
+ "img",
1299
+ {
1300
+ src: iconUrl,
1301
+ alt: "",
1302
+ style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
1303
+ }
1304
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1305
+ "div",
1306
+ {
1307
+ style: {
1308
+ width: 40,
1309
+ height: 40,
1310
+ borderRadius: 10,
1311
+ background: "var(--hook-color-primary)",
1312
+ color: "#fff",
1313
+ display: "flex",
1314
+ alignItems: "center",
1315
+ justifyContent: "center",
1316
+ fontSize: 18,
1317
+ fontWeight: 700,
1318
+ flexShrink: 0
1319
+ },
1320
+ children: name.charAt(0).toUpperCase()
1321
+ }
1322
+ ),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1324
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
1326
+ ] }),
1327
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1328
+ "button",
1329
+ {
1330
+ "data-testid": "install-prompt-cta-desktop",
1331
+ type: "button",
1332
+ onClick: () => void actions.promptInstall(),
1333
+ style: {
1334
+ padding: "8px 14px",
1335
+ background: "var(--hook-color-primary)",
1336
+ color: "#fff",
1337
+ border: "none",
1338
+ borderRadius: 999,
1339
+ fontSize: 13,
1340
+ fontWeight: 600,
1341
+ cursor: "pointer",
1342
+ display: "inline-flex",
1343
+ alignItems: "center",
1344
+ gap: 6,
1345
+ flexShrink: 0
1346
+ },
1347
+ children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DownloadIcon, { size: 14 }),
1349
+ copy.cta
1350
+ ]
1351
+ }
1352
+ ),
1353
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1354
+ "button",
1355
+ {
1356
+ "data-testid": "install-prompt-desktop-close",
1357
+ type: "button",
1358
+ onClick: actions.dismissPermanent,
1359
+ "aria-label": copy.close,
1360
+ style: {
1361
+ background: "transparent",
1362
+ border: "none",
1363
+ cursor: "pointer",
1364
+ color: "#888",
1365
+ padding: 4,
1366
+ flexShrink: 0
1367
+ },
1368
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(XIcon, { size: 16 })
1369
+ }
1370
+ )
1371
+ ]
1372
+ }
1373
+ );
1374
+ }
1375
+ var bannerStyle = {
1376
+ position: "fixed",
1377
+ bottom: 24,
1378
+ right: 24,
1379
+ zIndex: 1e4,
1380
+ display: "flex",
1381
+ alignItems: "center",
1382
+ gap: 12,
1383
+ padding: "12px 16px",
1384
+ background: "#fff",
1385
+ border: "1px solid rgba(0,0,0,0.08)",
1386
+ borderRadius: 16,
1387
+ boxShadow: "0 10px 30px rgba(0,0,0,0.12)",
1388
+ maxWidth: 400
1389
+ };
1390
+
1391
+ // src/components/InstallGate/InstallGate.tsx
1392
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1393
+ function InstallGate({ children }) {
1394
+ const { slug, features_enabled } = useTemplateConfig();
1395
+ const enabled = features_enabled.includes("install_prompt");
1396
+ const installState = useInstallPrompt(slug);
1397
+ const shouldBlock = enabled && shouldBlockInstall(installState);
1398
+ const trackedRef = (0, import_react8.useRef)(null);
1399
+ (0, import_react8.useEffect)(() => {
1400
+ if (!shouldBlock) return;
1401
+ if (typeof window === "undefined") return;
1402
+ const variantKey = `${slug}:${installState.variant}`;
1403
+ if (trackedRef.current === variantKey) return;
1404
+ trackedRef.current = variantKey;
1405
+ window.posthog?.capture?.("pwa_install_splash_shown", {
1406
+ slug,
1407
+ platform: installState.platform,
1408
+ browser: installState.iosBrowser ?? installState.androidBrowser ?? null,
1409
+ in_app_app: installState.inAppApp,
1410
+ variant: installState.variant
1411
+ });
1412
+ }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
1413
+ if (!enabled) return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children });
1414
+ if (installState.isInstalled) return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children });
1415
+ if (installState.variant === "desktop") {
1416
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1417
+ children,
1418
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DesktopVariant, { state: installState, actions: installState })
1419
+ ] });
1420
+ }
1421
+ if (!shouldBlock) return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children });
1422
+ switch (installState.variant) {
1423
+ case "android-native":
1424
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AndroidNativeVariant, { state: installState, actions: installState });
1425
+ case "android-manual":
1426
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AndroidManualVariant, { state: installState, actions: installState });
1427
+ case "ios-safari":
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IOSafariVariant, { state: installState, actions: installState });
1429
+ case "ios-other":
1430
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IOSOtherVariant, { state: installState, actions: installState });
1431
+ case "in-app":
1432
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(InAppBrowserVariant, { state: installState, actions: installState });
1433
+ case "none":
1434
+ default:
1435
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children });
1436
+ }
1437
+ }
1438
+
1439
+ // src/defaults/ErrorBoundary.tsx
1440
+ var import_react9 = require("react");
1441
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1442
+ var ErrorBoundary = class extends import_react9.Component {
217
1443
  state = { error: null };
218
1444
  static getDerivedStateFromError(error) {
219
1445
  return { error };
@@ -223,17 +1449,17 @@ var ErrorBoundary = class extends import_react5.Component {
223
1449
  }
224
1450
  render() {
225
1451
  if (this.state.error) {
226
- return this.props.fallback ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
227
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { children: "Algo deu errado" }),
228
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
1452
+ return this.props.fallback ?? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
1453
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { children: "Algo deu errado" }),
1454
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
229
1455
  ] });
230
1456
  }
231
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: this.props.children });
1457
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: this.props.children });
232
1458
  }
233
1459
  };
234
1460
 
235
1461
  // src/hooks/useLoginForm.ts
236
- var import_react6 = require("react");
1462
+ var import_react10 = require("react");
237
1463
  var import_sdk5 = require("@hook-sdk/sdk");
238
1464
 
239
1465
  // src/errors.ts
@@ -270,22 +1496,22 @@ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
270
1496
  var MIN_PASSWORD = 8;
271
1497
  function useLoginForm() {
272
1498
  const { auth } = (0, import_sdk5.useHook)();
273
- const [email, setEmail] = (0, import_react6.useState)("");
274
- const [password, setPassword] = (0, import_react6.useState)("");
275
- const [submitting, setSubmitting] = (0, import_react6.useState)(false);
276
- const [error, setError] = (0, import_react6.useState)(null);
277
- const emailError = (0, import_react6.useMemo)(() => {
1499
+ const [email, setEmail] = (0, import_react10.useState)("");
1500
+ const [password, setPassword] = (0, import_react10.useState)("");
1501
+ const [submitting, setSubmitting] = (0, import_react10.useState)(false);
1502
+ const [error, setError] = (0, import_react10.useState)(null);
1503
+ const emailError = (0, import_react10.useMemo)(() => {
278
1504
  if (email.length === 0) return null;
279
1505
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
280
1506
  return null;
281
1507
  }, [email]);
282
- const passwordError = (0, import_react6.useMemo)(() => {
1508
+ const passwordError = (0, import_react10.useMemo)(() => {
283
1509
  if (password.length === 0) return null;
284
1510
  if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
285
1511
  return null;
286
1512
  }, [password]);
287
1513
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && emailError === null && passwordError === null && !submitting;
288
- const submit = (0, import_react6.useCallback)(async () => {
1514
+ const submit = (0, import_react10.useCallback)(async () => {
289
1515
  if (!canSubmit) return false;
290
1516
  setSubmitting(true);
291
1517
  setError(null);
@@ -309,25 +1535,183 @@ function useLoginForm() {
309
1535
  submit,
310
1536
  submitting,
311
1537
  canSubmit,
312
- error
1538
+ error,
1539
+ loginWithGoogle: () => auth.loginWithGoogle()
313
1540
  };
314
1541
  }
315
1542
 
1543
+ // src/internal/GoogleSignInButton.tsx
1544
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1545
+ function GoogleSignInButton({
1546
+ onClick,
1547
+ testId = "oauth-google",
1548
+ label = "Continuar com Google"
1549
+ }) {
1550
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1551
+ "button",
1552
+ {
1553
+ "data-testid": testId,
1554
+ type: "button",
1555
+ onClick,
1556
+ style: {
1557
+ width: "100%",
1558
+ padding: "10px 12px",
1559
+ display: "flex",
1560
+ alignItems: "center",
1561
+ justifyContent: "center",
1562
+ gap: 10,
1563
+ background: "#fff",
1564
+ color: "#1f1f1f",
1565
+ border: "1px solid #dadce0",
1566
+ borderRadius: 8,
1567
+ cursor: "pointer",
1568
+ fontSize: 14,
1569
+ fontWeight: 500,
1570
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1571
+ },
1572
+ children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(GoogleGlyph, {}),
1574
+ label
1575
+ ]
1576
+ }
1577
+ );
1578
+ }
1579
+ function GoogleGlyph() {
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1582
+ "path",
1583
+ {
1584
+ d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.874 2.684-6.615z",
1585
+ fill: "#4285F4"
1586
+ }
1587
+ ),
1588
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1589
+ "path",
1590
+ {
1591
+ d: "M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z",
1592
+ fill: "#34A853"
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1596
+ "path",
1597
+ {
1598
+ d: "M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z",
1599
+ fill: "#FBBC05"
1600
+ }
1601
+ ),
1602
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1603
+ "path",
1604
+ {
1605
+ d: "M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z",
1606
+ fill: "#EA4335"
1607
+ }
1608
+ )
1609
+ ] });
1610
+ }
1611
+
1612
+ // src/internal/OAuthErrorBanner.tsx
1613
+ var import_react11 = require("react");
1614
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1615
+ var ERROR_MESSAGES = {
1616
+ invalid_state: "Sess\xE3o expirou, tente de novo.",
1617
+ access_denied: "Voc\xEA cancelou o login com Google.",
1618
+ provider_error: "O Google recusou a autentica\xE7\xE3o. Tente de novo em alguns segundos.",
1619
+ invalid_return_to: "Link inv\xE1lido. Tente entrar novamente."
1620
+ };
1621
+ function readErrorCode() {
1622
+ if (typeof window === "undefined") return null;
1623
+ const code = new URLSearchParams(window.location.search).get("oauth_error");
1624
+ if (!code) return null;
1625
+ return code;
1626
+ }
1627
+ function stripErrorFromUrl() {
1628
+ if (typeof window === "undefined") return;
1629
+ const url = new URL(window.location.href);
1630
+ url.searchParams.delete("oauth_error");
1631
+ window.history.replaceState({}, "", url.toString());
1632
+ }
1633
+ function OAuthErrorBanner() {
1634
+ const [code, setCode] = (0, import_react11.useState)(() => readErrorCode());
1635
+ (0, import_react11.useEffect)(() => {
1636
+ if (code !== null) stripErrorFromUrl();
1637
+ }, [code]);
1638
+ if (!code) return null;
1639
+ const message = ERROR_MESSAGES[code] ?? "N\xE3o conseguimos conectar ao Google. Tente de novo.";
1640
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1641
+ "div",
1642
+ {
1643
+ role: "alert",
1644
+ "data-testid": "oauth-error-banner",
1645
+ style: {
1646
+ padding: "10px 12px",
1647
+ marginBottom: 16,
1648
+ background: "#fce8e6",
1649
+ color: "#a50e0e",
1650
+ borderRadius: 8,
1651
+ fontSize: 14
1652
+ },
1653
+ children: [
1654
+ message,
1655
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1656
+ "button",
1657
+ {
1658
+ type: "button",
1659
+ onClick: () => setCode(null),
1660
+ "aria-label": "Fechar",
1661
+ style: {
1662
+ float: "right",
1663
+ background: "none",
1664
+ border: "none",
1665
+ color: "#a50e0e",
1666
+ cursor: "pointer",
1667
+ fontSize: 16,
1668
+ lineHeight: 1,
1669
+ padding: 0
1670
+ },
1671
+ children: "\xD7"
1672
+ }
1673
+ )
1674
+ ]
1675
+ }
1676
+ );
1677
+ }
1678
+
316
1679
  // src/defaults/DefaultLoginScreen.tsx
317
- var import_jsx_runtime8 = require("react/jsx-runtime");
1680
+ var import_jsx_runtime19 = require("react/jsx-runtime");
318
1681
  function DefaultLoginScreen({ onNavigate }) {
319
1682
  const { name } = useTemplateConfig();
320
1683
  const f = useLoginForm();
321
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
322
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
323
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
324
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("form", { onSubmit: (e) => {
1684
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1685
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
1686
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(OAuthErrorBanner, {}),
1688
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
1689
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1690
+ "div",
1691
+ {
1692
+ "aria-hidden": "true",
1693
+ style: {
1694
+ display: "flex",
1695
+ alignItems: "center",
1696
+ gap: 8,
1697
+ margin: "16px 0",
1698
+ color: "rgba(0,0,0,0.45)",
1699
+ fontSize: 12
1700
+ },
1701
+ children: [
1702
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1703
+ "ou",
1704
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1705
+ ]
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("form", { onSubmit: (e) => {
325
1709
  e.preventDefault();
326
1710
  void f.submit();
327
1711
  }, children: [
328
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
329
1713
  "E-mail",
330
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1714
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
331
1715
  "input",
332
1716
  {
333
1717
  "data-testid": "login-email",
@@ -337,11 +1721,11 @@ function DefaultLoginScreen({ onNavigate }) {
337
1721
  style: { display: "block", width: "100%" }
338
1722
  }
339
1723
  ),
340
- f.emailError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
1724
+ f.emailError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
341
1725
  ] }),
342
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1726
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
343
1727
  "Senha",
344
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1728
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
345
1729
  "input",
346
1730
  {
347
1731
  "data-testid": "login-password",
@@ -351,10 +1735,10 @@ function DefaultLoginScreen({ onNavigate }) {
351
1735
  style: { display: "block", width: "100%" }
352
1736
  }
353
1737
  ),
354
- f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
1738
+ f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
355
1739
  ] }),
356
- f.error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
357
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1740
+ f.error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
358
1742
  "button",
359
1743
  {
360
1744
  "data-testid": "login-submit",
@@ -373,42 +1757,42 @@ function DefaultLoginScreen({ onNavigate }) {
373
1757
  }
374
1758
  )
375
1759
  ] }),
376
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
377
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
378
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
1760
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
1761
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
1762
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
379
1763
  ] })
380
1764
  ] });
381
1765
  }
382
1766
 
383
1767
  // src/hooks/useSignupForm.ts
384
- var import_react7 = require("react");
1768
+ var import_react12 = require("react");
385
1769
  var import_sdk6 = require("@hook-sdk/sdk");
386
1770
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
387
1771
  var MIN_PASSWORD2 = 8;
388
1772
  function useSignupForm() {
389
1773
  const { auth } = (0, import_sdk6.useHook)();
390
- const [name, setName] = (0, import_react7.useState)("");
391
- const [email, setEmail] = (0, import_react7.useState)("");
392
- const [password, setPassword] = (0, import_react7.useState)("");
393
- const [submitting, setSubmitting] = (0, import_react7.useState)(false);
394
- const [error, setError] = (0, import_react7.useState)(null);
395
- const nameError = (0, import_react7.useMemo)(() => {
1774
+ const [name, setName] = (0, import_react12.useState)("");
1775
+ const [email, setEmail] = (0, import_react12.useState)("");
1776
+ const [password, setPassword] = (0, import_react12.useState)("");
1777
+ const [submitting, setSubmitting] = (0, import_react12.useState)(false);
1778
+ const [error, setError] = (0, import_react12.useState)(null);
1779
+ const nameError = (0, import_react12.useMemo)(() => {
396
1780
  if (name.length === 0) return null;
397
1781
  if (name.trim().length < 2) return "Nome muito curto.";
398
1782
  return null;
399
1783
  }, [name]);
400
- const emailError = (0, import_react7.useMemo)(() => {
1784
+ const emailError = (0, import_react12.useMemo)(() => {
401
1785
  if (email.length === 0) return null;
402
1786
  if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
403
1787
  return null;
404
1788
  }, [email]);
405
- const passwordError = (0, import_react7.useMemo)(() => {
1789
+ const passwordError = (0, import_react12.useMemo)(() => {
406
1790
  if (password.length === 0) return null;
407
1791
  if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
408
1792
  return null;
409
1793
  }, [password]);
410
1794
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
411
- const submit = (0, import_react7.useCallback)(async () => {
1795
+ const submit = (0, import_react12.useCallback)(async () => {
412
1796
  if (!canSubmit) return false;
413
1797
  setSubmitting(true);
414
1798
  setError(null);
@@ -435,61 +1819,83 @@ function useSignupForm() {
435
1819
  submit,
436
1820
  submitting,
437
1821
  canSubmit,
438
- error
1822
+ error,
1823
+ loginWithGoogle: () => auth.loginWithGoogle()
439
1824
  };
440
1825
  }
441
1826
 
442
1827
  // src/defaults/DefaultSignupScreen.tsx
443
- var import_jsx_runtime9 = require("react/jsx-runtime");
1828
+ var import_jsx_runtime20 = require("react/jsx-runtime");
444
1829
  function DefaultSignupScreen({ onNavigate }) {
445
1830
  const { name } = useTemplateConfig();
446
1831
  const f = useSignupForm();
447
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
448
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
449
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
450
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("form", { onSubmit: (e) => {
1832
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1833
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
1834
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
1835
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OAuthErrorBanner, {}),
1836
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
1837
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1838
+ "div",
1839
+ {
1840
+ "aria-hidden": "true",
1841
+ style: {
1842
+ display: "flex",
1843
+ alignItems: "center",
1844
+ gap: 8,
1845
+ margin: "16px 0",
1846
+ color: "rgba(0,0,0,0.45)",
1847
+ fontSize: 12
1848
+ },
1849
+ children: [
1850
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1851
+ "ou",
1852
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1853
+ ]
1854
+ }
1855
+ ),
1856
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("form", { onSubmit: (e) => {
451
1857
  e.preventDefault();
452
1858
  void f.submit();
453
1859
  }, children: [
454
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1860
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
455
1861
  "Nome",
456
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
457
- f.nameError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("small", { style: { color: "#c00" }, children: f.nameError })
1862
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
1863
+ f.nameError && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("small", { style: { color: "#c00" }, children: f.nameError })
458
1864
  ] }),
459
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1865
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
460
1866
  "E-mail",
461
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
462
- f.emailError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
1867
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1868
+ f.emailError && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
463
1869
  ] }),
464
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1870
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
465
1871
  "Senha",
466
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
467
- f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
1872
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
1873
+ f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
468
1874
  ] }),
469
- f.error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
470
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
1875
+ f.error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1876
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
471
1877
  ] }),
472
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
1878
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
473
1879
  ] });
474
1880
  }
475
1881
 
476
1882
  // src/hooks/useForgotForm.ts
477
- var import_react8 = require("react");
1883
+ var import_react13 = require("react");
478
1884
  var import_sdk7 = require("@hook-sdk/sdk");
479
1885
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
480
1886
  function useForgotForm() {
481
1887
  const { auth } = (0, import_sdk7.useHook)();
482
- const [email, setEmail] = (0, import_react8.useState)("");
483
- const [submitting, setSubmitting] = (0, import_react8.useState)(false);
484
- const [sent, setSent] = (0, import_react8.useState)(false);
485
- const [error, setError] = (0, import_react8.useState)(null);
486
- const emailError = (0, import_react8.useMemo)(() => {
1888
+ const [email, setEmail] = (0, import_react13.useState)("");
1889
+ const [submitting, setSubmitting] = (0, import_react13.useState)(false);
1890
+ const [sent, setSent] = (0, import_react13.useState)(false);
1891
+ const [error, setError] = (0, import_react13.useState)(null);
1892
+ const emailError = (0, import_react13.useMemo)(() => {
487
1893
  if (email.length === 0) return null;
488
1894
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
489
1895
  return null;
490
1896
  }, [email]);
491
1897
  const canSubmit = email.length > 0 && emailError === null && !submitting;
492
- const submit = (0, import_react8.useCallback)(async () => {
1898
+ const submit = (0, import_react13.useCallback)(async () => {
493
1899
  if (!canSubmit) return false;
494
1900
  setSubmitting(true);
495
1901
  setError(null);
@@ -517,66 +1923,66 @@ function useForgotForm() {
517
1923
  }
518
1924
 
519
1925
  // src/defaults/DefaultForgotScreen.tsx
520
- var import_jsx_runtime10 = require("react/jsx-runtime");
1926
+ var import_jsx_runtime21 = require("react/jsx-runtime");
521
1927
  function DefaultForgotScreen({ onNavigate }) {
522
1928
  const { name } = useTemplateConfig();
523
1929
  const f = useForgotForm();
524
1930
  if (f.sent) {
525
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
526
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { children: "Verifique seu e-mail" }),
527
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
528
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
1931
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
1932
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h1", { children: "Verifique seu e-mail" }),
1933
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
1934
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
529
1935
  ] });
530
1936
  }
531
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
532
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
533
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
534
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit: (e) => {
1937
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1938
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
1939
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
1940
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("form", { onSubmit: (e) => {
535
1941
  e.preventDefault();
536
1942
  void f.submit();
537
1943
  }, children: [
538
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
1944
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
539
1945
  "E-mail",
540
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
541
- f.emailError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
1946
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1947
+ f.emailError && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("small", { style: { color: "#c00" }, children: f.emailError })
542
1948
  ] }),
543
- f.error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
544
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
1949
+ f.error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1950
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
545
1951
  ] }),
546
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
1952
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
547
1953
  ] });
548
1954
  }
549
1955
 
550
1956
  // src/hooks/useResetForm.ts
551
- var import_react9 = require("react");
1957
+ var import_react14 = require("react");
552
1958
  var import_sdk8 = require("@hook-sdk/sdk");
553
1959
  var MIN_PASSWORD3 = 12;
554
1960
  function useResetForm() {
555
1961
  const { auth } = (0, import_sdk8.useHook)();
556
- const [token, setToken] = (0, import_react9.useState)(null);
557
- const [password, setPassword] = (0, import_react9.useState)("");
558
- const [confirm, setConfirm] = (0, import_react9.useState)("");
559
- const [submitting, setSubmitting] = (0, import_react9.useState)(false);
560
- const [done, setDone] = (0, import_react9.useState)(false);
561
- const [error, setError] = (0, import_react9.useState)(null);
562
- (0, import_react9.useEffect)(() => {
1962
+ const [token, setToken] = (0, import_react14.useState)(null);
1963
+ const [password, setPassword] = (0, import_react14.useState)("");
1964
+ const [confirm, setConfirm] = (0, import_react14.useState)("");
1965
+ const [submitting, setSubmitting] = (0, import_react14.useState)(false);
1966
+ const [done, setDone] = (0, import_react14.useState)(false);
1967
+ const [error, setError] = (0, import_react14.useState)(null);
1968
+ (0, import_react14.useEffect)(() => {
563
1969
  if (typeof window === "undefined") return;
564
1970
  const params = new URLSearchParams(window.location.search);
565
1971
  const t = params.get("token");
566
1972
  setToken(t && t.length > 0 ? t : null);
567
1973
  }, []);
568
- const passwordError = (0, import_react9.useMemo)(() => {
1974
+ const passwordError = (0, import_react14.useMemo)(() => {
569
1975
  if (password.length === 0) return null;
570
1976
  if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
571
1977
  return null;
572
1978
  }, [password]);
573
- const confirmError = (0, import_react9.useMemo)(() => {
1979
+ const confirmError = (0, import_react14.useMemo)(() => {
574
1980
  if (confirm.length === 0) return null;
575
1981
  if (confirm !== password) return "Senhas n\xE3o coincidem.";
576
1982
  return null;
577
1983
  }, [confirm, password]);
578
1984
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
579
- const submit = (0, import_react9.useCallback)(async () => {
1985
+ const submit = (0, import_react14.useCallback)(async () => {
580
1986
  if (!canSubmit || token === null) return;
581
1987
  setSubmitting(true);
582
1988
  setError(null);
@@ -612,67 +2018,67 @@ function useResetForm() {
612
2018
  }
613
2019
 
614
2020
  // src/defaults/DefaultResetScreen.tsx
615
- var import_jsx_runtime11 = require("react/jsx-runtime");
2021
+ var import_jsx_runtime22 = require("react/jsx-runtime");
616
2022
  function DefaultResetScreen({ onNavigate }) {
617
2023
  const { name } = useTemplateConfig();
618
2024
  const f = useResetForm();
619
2025
  if (f.done) {
620
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
621
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { children: "Senha alterada" }),
622
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
623
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h1", { children: "Senha alterada" }),
2028
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
2029
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
624
2030
  ] });
625
2031
  }
626
2032
  if (f.token === null) {
627
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
628
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { children: "Link inv\xE1lido" }),
629
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
630
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
2033
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
2034
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h1", { children: "Link inv\xE1lido" }),
2035
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
2036
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
631
2037
  ] });
632
2038
  }
633
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
634
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
635
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
636
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: (e) => {
2039
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
2040
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h1", { style: { marginBottom: 8 }, children: name }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
2042
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("form", { onSubmit: (e) => {
637
2043
  e.preventDefault();
638
2044
  void f.submit();
639
2045
  }, children: [
640
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
2046
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
641
2047
  "Nova senha",
642
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
643
- f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
2048
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
2049
+ f.passwordError && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("small", { style: { color: "#c00" }, children: f.passwordError })
644
2050
  ] }),
645
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
2051
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { style: { display: "block", marginBottom: 12 }, children: [
646
2052
  "Confirmar senha",
647
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
648
- f.confirmError && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("small", { style: { color: "#c00" }, children: f.confirmError })
2053
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
2054
+ f.confirmError && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("small", { style: { color: "#c00" }, children: f.confirmError })
649
2055
  ] }),
650
- f.error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
651
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
2056
+ f.error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
2057
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
652
2058
  ] })
653
2059
  ] });
654
2060
  }
655
2061
 
656
2062
  // src/defaults/DefaultPaywall.tsx
657
- var import_react10 = require("react");
658
- var import_jsx_runtime12 = require("react/jsx-runtime");
2063
+ var import_react15 = require("react");
2064
+ var import_jsx_runtime23 = require("react/jsx-runtime");
659
2065
  function DefaultPaywall() {
660
2066
  const config = useTemplateConfig();
661
2067
  const { checkout, opening, error } = usePaywallState();
662
2068
  const p = config.subscription.paywall_config;
663
- const [cpf, setCpf] = (0, import_react10.useState)("");
2069
+ const [cpf, setCpf] = (0, import_react15.useState)("");
664
2070
  const cpfDigits = cpf.replace(/\D/g, "");
665
2071
  const canCheckout = cpfDigits.length === 11 && !opening;
666
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
667
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { style: { marginBottom: 8 }, children: p.title }),
668
- p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
669
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
671
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: b })
2072
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
2073
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h1", { style: { marginBottom: 8 }, children: p.title }),
2074
+ p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
2075
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
2076
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
2077
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: b })
672
2078
  ] }, b)) }),
673
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
674
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
675
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2079
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
2080
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
2081
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
676
2082
  "input",
677
2083
  {
678
2084
  "data-testid": "paywall-cpf",
@@ -685,8 +2091,8 @@ function DefaultPaywall() {
685
2091
  }
686
2092
  )
687
2093
  ] }),
688
- error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
689
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2094
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
2095
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
690
2096
  "button",
691
2097
  {
692
2098
  "data-testid": "paywall-cta",
@@ -707,21 +2113,21 @@ function DefaultPaywall() {
707
2113
  children: opening ? "Abrindo..." : p.cta
708
2114
  }
709
2115
  ),
710
- p.priceHint && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
711
- p.footerNote && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
2116
+ p.priceHint && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
2117
+ p.footerNote && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
712
2118
  ] });
713
2119
  }
714
2120
 
715
2121
  // src/AppRoot.tsx
716
- var import_jsx_runtime13 = require("react/jsx-runtime");
2122
+ var import_jsx_runtime24 = require("react/jsx-runtime");
717
2123
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
718
2124
  function PaymentReturnHandler({ children }) {
719
2125
  const { subscription } = (0, import_sdk9.useHook)();
720
- const subRef = (0, import_react11.useRef)(subscription);
2126
+ const subRef = (0, import_react16.useRef)(subscription);
721
2127
  subRef.current = subscription;
722
- const runIdRef = (0, import_react11.useRef)(0);
723
- const [state, setState] = (0, import_react11.useState)("idle");
724
- const runPoll = (0, import_react11.useCallback)(() => {
2128
+ const runIdRef = (0, import_react16.useRef)(0);
2129
+ const [state, setState] = (0, import_react16.useState)("idle");
2130
+ const runPoll = (0, import_react16.useCallback)(() => {
725
2131
  const runId = ++runIdRef.current;
726
2132
  setState("confirming");
727
2133
  let attempts = 0;
@@ -750,7 +2156,7 @@ function PaymentReturnHandler({ children }) {
750
2156
  };
751
2157
  void tick();
752
2158
  }, []);
753
- (0, import_react11.useEffect)(() => {
2159
+ (0, import_react16.useEffect)(() => {
754
2160
  if (typeof window === "undefined") return;
755
2161
  const url = new URL(window.location.href);
756
2162
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -760,20 +2166,20 @@ function PaymentReturnHandler({ children }) {
760
2166
  };
761
2167
  }, [runPoll]);
762
2168
  if (state === "confirming") {
763
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2169
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
764
2170
  "div",
765
2171
  {
766
2172
  role: "status",
767
2173
  "aria-live": "polite",
768
- style: overlayStyle,
2174
+ style: overlayStyle2,
769
2175
  children: "Confirmando pagamento\u2026"
770
2176
  }
771
2177
  );
772
2178
  }
773
2179
  if (state === "waiting") {
774
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
775
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
776
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2180
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2181
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2182
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
777
2183
  "button",
778
2184
  {
779
2185
  type: "button",
@@ -784,9 +2190,9 @@ function PaymentReturnHandler({ children }) {
784
2190
  )
785
2191
  ] }) });
786
2192
  }
787
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children });
2193
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children });
788
2194
  }
789
- var overlayStyle = {
2195
+ var overlayStyle2 = {
790
2196
  position: "fixed",
791
2197
  inset: 0,
792
2198
  display: "flex",
@@ -817,14 +2223,14 @@ function AppRoot({
817
2223
  Reset = DefaultResetScreen,
818
2224
  Paywall = DefaultPaywall
819
2225
  }) {
820
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PaymentReturnHandler, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TemplateConfigProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PersistedKeysPrefetch, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(SubscriptionGate, { Paywall, children: [
2226
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaymentReturnHandler, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TemplateConfigProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(InstallGate, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PersistedKeysPrefetch, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(SubscriptionGate, { Paywall, children: [
821
2227
  children,
822
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PushPrompt, {})
823
- ] }) }) }) }) }) }) });
2228
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PushPrompt, {})
2229
+ ] }) }) }) }) }) }) }) });
824
2230
  }
825
2231
 
826
2232
  // src/hooks/usePush.ts
827
- var import_react12 = require("react");
2233
+ var import_react17 = require("react");
828
2234
  var import_sdk10 = require("@hook-sdk/sdk");
829
2235
  function detectIosNeedsInstall() {
830
2236
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
@@ -852,11 +2258,11 @@ function deriveState(push) {
852
2258
  }
853
2259
  function usePush() {
854
2260
  const { push } = (0, import_sdk10.useHook)();
855
- const [state, setState] = (0, import_react12.useState)(() => deriveState(push));
856
- (0, import_react12.useEffect)(() => {
2261
+ const [state, setState] = (0, import_react17.useState)(() => deriveState(push));
2262
+ (0, import_react17.useEffect)(() => {
857
2263
  setState(deriveState(push));
858
2264
  }, [push]);
859
- const subscribe = (0, import_react12.useCallback)(async () => {
2265
+ const subscribe = (0, import_react17.useCallback)(async () => {
860
2266
  try {
861
2267
  await push.subscribe();
862
2268
  setState({ kind: "subscribed" });
@@ -868,7 +2274,7 @@ function usePush() {
868
2274
  throw e;
869
2275
  }
870
2276
  }, [push]);
871
- const unsubscribe = (0, import_react12.useCallback)(async () => {
2277
+ const unsubscribe = (0, import_react17.useCallback)(async () => {
872
2278
  try {
873
2279
  await push.unsubscribe();
874
2280
  setState({ kind: "prompt" });
@@ -881,31 +2287,31 @@ function usePush() {
881
2287
  }
882
2288
 
883
2289
  // src/components/PushPrompt.tsx
884
- var import_jsx_runtime14 = require("react/jsx-runtime");
2290
+ var import_jsx_runtime25 = require("react/jsx-runtime");
885
2291
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
886
2292
  const { state, subscribe } = usePush();
887
2293
  if (state.kind === "subscribed") return null;
888
2294
  if (state.kind === "ios_needs_install") {
889
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
890
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { children: texts.iosInstallTitle }),
891
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: texts.iosInstallBody }),
892
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2295
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2296
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { children: texts.iosInstallTitle }),
2297
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { children: texts.iosInstallBody }),
2298
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
893
2299
  ] });
894
2300
  }
895
2301
  if (state.kind === "denied") {
896
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
897
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { children: texts.deniedTitle }),
898
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: texts.deniedBody })
2302
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2303
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { children: texts.deniedTitle }),
2304
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { children: texts.deniedBody })
899
2305
  ] });
900
2306
  }
901
2307
  if (state.kind === "unsupported") {
902
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className, role: "region", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: texts.unsupportedBody }) });
2308
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className, role: "region", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { children: texts.unsupportedBody }) });
903
2309
  }
904
2310
  if (state.kind === "error") {
905
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: state.message }) });
2311
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { children: state.message }) });
906
2312
  }
907
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className, role: "region", children: [
908
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2313
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className, role: "region", children: [
2314
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
909
2315
  "button",
910
2316
  {
911
2317
  type: "button",
@@ -919,27 +2325,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
919
2325
  children: texts.cta
920
2326
  }
921
2327
  ),
922
- onDeclined && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2328
+ onDeclined && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
923
2329
  ] });
924
2330
  }
925
2331
 
926
2332
  // src/defaults/EmptyState.tsx
927
- var import_jsx_runtime15 = require("react/jsx-runtime");
2333
+ var import_jsx_runtime26 = require("react/jsx-runtime");
928
2334
  function EmptyState({ title, description, action }) {
929
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
930
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { style: { marginBottom: 8 }, children: title }),
931
- description && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: { opacity: 0.7 }, children: description }),
932
- action && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { marginTop: 16 }, children: action })
2335
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { style: { marginBottom: 8 }, children: title }),
2337
+ description && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { style: { opacity: 0.7 }, children: description }),
2338
+ action && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: { marginTop: 16 }, children: action })
933
2339
  ] });
934
2340
  }
935
2341
 
936
2342
  // src/hooks/useAuthPrimitives.ts
937
- var import_react13 = require("react");
2343
+ var import_react18 = require("react");
938
2344
  var import_sdk11 = require("@hook-sdk/sdk");
939
2345
  var warned = false;
940
2346
  function useAuthPrimitives() {
941
2347
  const { auth } = (0, import_sdk11.useHook)();
942
- (0, import_react13.useEffect)(() => {
2348
+ (0, import_react18.useEffect)(() => {
943
2349
  if (!warned && process.env.NODE_ENV !== "production") {
944
2350
  warned = true;
945
2351
  console.warn(
@@ -970,14 +2376,14 @@ function useSubscription() {
970
2376
  }
971
2377
 
972
2378
  // src/hooks/useReminders.ts
973
- var import_react14 = require("react");
2379
+ var import_react19 = require("react");
974
2380
  var import_sdk13 = require("@hook-sdk/sdk");
975
2381
  function useReminders() {
976
2382
  const { push } = (0, import_sdk13.useHook)();
977
2383
  const r = push.reminders;
978
- const [reminders, setReminders] = (0, import_react14.useState)([]);
979
- const [loading, setLoading] = (0, import_react14.useState)(true);
980
- const reload = (0, import_react14.useCallback)(async () => {
2384
+ const [reminders, setReminders] = (0, import_react19.useState)([]);
2385
+ const [loading, setLoading] = (0, import_react19.useState)(true);
2386
+ const reload = (0, import_react19.useCallback)(async () => {
981
2387
  setLoading(true);
982
2388
  try {
983
2389
  const next = await r.list();
@@ -986,38 +2392,38 @@ function useReminders() {
986
2392
  setLoading(false);
987
2393
  }
988
2394
  }, [r]);
989
- (0, import_react14.useEffect)(() => {
2395
+ (0, import_react19.useEffect)(() => {
990
2396
  void reload();
991
2397
  }, [reload]);
992
- const setReminder = (0, import_react14.useCallback)(async (input) => {
2398
+ const setReminder = (0, import_react19.useCallback)(async (input) => {
993
2399
  await r.set(input);
994
2400
  await reload();
995
2401
  }, [r, reload]);
996
- const deleteReminder = (0, import_react14.useCallback)(async (slot) => {
2402
+ const deleteReminder = (0, import_react19.useCallback)(async (slot) => {
997
2403
  await r.delete(slot);
998
2404
  await reload();
999
2405
  }, [r, reload]);
1000
- const schedule = (0, import_react14.useCallback)(async (items) => {
2406
+ const schedule = (0, import_react19.useCallback)(async (items) => {
1001
2407
  return r.schedule(items);
1002
2408
  }, [r]);
1003
- const setFallbacks = (0, import_react14.useCallback)(async (items) => {
2409
+ const setFallbacks = (0, import_react19.useCallback)(async (items) => {
1004
2410
  return r.setFallbacks(items);
1005
2411
  }, [r]);
1006
2412
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
1007
2413
  }
1008
2414
 
1009
2415
  // src/hooks/useToast.ts
1010
- var import_react15 = require("react");
2416
+ var import_react20 = require("react");
1011
2417
  function useToast() {
1012
- const [items, setItems] = (0, import_react15.useState)([]);
1013
- const show = (0, import_react15.useCallback)((message, kind = "info") => {
2418
+ const [items, setItems] = (0, import_react20.useState)([]);
2419
+ const show = (0, import_react20.useCallback)((message, kind = "info") => {
1014
2420
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1015
2421
  setItems((prev) => [...prev, { id, message, kind }]);
1016
2422
  setTimeout(() => {
1017
2423
  setItems((prev) => prev.filter((t) => t.id !== id));
1018
2424
  }, 4e3);
1019
2425
  }, []);
1020
- const dismiss = (0, import_react15.useCallback)((id) => {
2426
+ const dismiss = (0, import_react20.useCallback)((id) => {
1021
2427
  setItems((prev) => prev.filter((t) => t.id !== id));
1022
2428
  }, []);
1023
2429
  return { items, show, dismiss };
@@ -1032,11 +2438,21 @@ function useToast() {
1032
2438
  DefaultSignupScreen,
1033
2439
  EmptyState,
1034
2440
  ErrorBoundary,
2441
+ InstallGate,
2442
+ InstallSplash,
1035
2443
  LoadingState,
1036
2444
  PushPrompt,
2445
+ detectAndroidBrowser,
2446
+ detectIOSBrowser,
2447
+ detectInAppApp,
2448
+ detectPlatform,
2449
+ detectStandalone,
2450
+ shouldBlockInstall,
2451
+ shouldShowPermanentOption,
1037
2452
  useAuth,
1038
2453
  useAuthPrimitives,
1039
2454
  useForgotForm,
2455
+ useInstallPrompt,
1040
2456
  useLoginForm,
1041
2457
  usePaywallState,
1042
2458
  usePush,