@hook-sdk/template 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/AppRoot.tsx
2
- import { useCallback as useCallback6, useEffect as useEffect5, useRef, useState as useState9 } from "react";
2
+ import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef2, useState as useState12 } from "react";
3
3
  import { useHook as useHook8 } from "@hook-sdk/sdk";
4
4
 
5
5
  // src/internal/TemplateConfigContext.tsx
@@ -164,9 +164,1302 @@ function PushPrompt() {
164
164
  return null;
165
165
  }
166
166
 
167
+ // src/components/InstallGate/InstallGate.tsx
168
+ import { useEffect as useEffect4, useRef } from "react";
169
+
170
+ // src/hooks/useInstallPrompt.ts
171
+ import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "react";
172
+ var IOS_RE = /iPad|iPhone|iPod/;
173
+ var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
174
+ var ANDROID_RE = /Android/;
175
+ var IN_APP_RE = /Instagram|FBAN|FBAV|BytedanceWebview|TikTok|Line\/|Twitter|Snapchat|Pinterest|LinkedIn|WhatsApp|MicroMessenger|Telegram/i;
176
+ var PERMANENT_DISMISS_REPROMPT_DAYS = 14;
177
+ var SESSION_SKIPS_BEFORE_PERMANENT_OPTION = 2;
178
+ var storageKey = {
179
+ sessionSkip: (slug) => `install:${slug}:session-skip`,
180
+ dismissedAt: (slug) => `install:${slug}:dismissed-at`,
181
+ installedAt: (slug) => `install:${slug}:installed-at`,
182
+ skipCount: (slug) => `install:${slug}:skip-count`
183
+ };
184
+ function detectPlatform(ua) {
185
+ if (IN_APP_RE.test(ua)) return "in-app";
186
+ const isIOS = IOS_RE.test(ua);
187
+ if (isIOS) {
188
+ const isSafari = /Safari/.test(ua) && !IOS_NON_SAFARI_RE.test(ua);
189
+ return isSafari ? "ios-safari" : "ios-other";
190
+ }
191
+ if (ANDROID_RE.test(ua)) return "android";
192
+ return "desktop";
193
+ }
194
+ function detectIOSBrowser(ua) {
195
+ if (!IOS_RE.test(ua)) return null;
196
+ if (/CriOS/.test(ua)) return "chrome";
197
+ if (/FxiOS/.test(ua)) return "firefox";
198
+ if (/EdgiOS/.test(ua)) return "edge";
199
+ if (/Safari/.test(ua)) return "safari";
200
+ return "other";
201
+ }
202
+ function detectAndroidBrowser(ua) {
203
+ if (!ANDROID_RE.test(ua)) return null;
204
+ if (/EdgA/.test(ua)) return "edge";
205
+ if (/OPR|OPT/.test(ua)) return "opera";
206
+ if (/SamsungBrowser/.test(ua)) return "samsung";
207
+ if (/Firefox/.test(ua)) return "firefox";
208
+ if (/Chrome/.test(ua)) return "chrome";
209
+ return "other";
210
+ }
211
+ function detectInAppApp(ua) {
212
+ if (!IN_APP_RE.test(ua)) return null;
213
+ if (/Instagram/i.test(ua)) return "instagram";
214
+ if (/FBAN|FBAV/.test(ua)) return "facebook";
215
+ if (/BytedanceWebview|TikTok/i.test(ua)) return "tiktok";
216
+ if (/WhatsApp/i.test(ua)) return "whatsapp";
217
+ if (/Twitter/i.test(ua)) return "twitter";
218
+ if (/LinkedIn/i.test(ua)) return "linkedin";
219
+ if (/Telegram/i.test(ua)) return "telegram";
220
+ if (/Line\//i.test(ua)) return "line";
221
+ if (/Snapchat/i.test(ua)) return "snapchat";
222
+ if (/Pinterest/i.test(ua)) return "pinterest";
223
+ if (/MicroMessenger/i.test(ua)) return "wechat";
224
+ return "other";
225
+ }
226
+ function detectStandalone() {
227
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
228
+ return { installed: false, source: null };
229
+ }
230
+ const mm = window.matchMedia?.("(display-mode: standalone)");
231
+ if (mm?.matches) return { installed: true, source: "match_media" };
232
+ const fs = window.matchMedia?.("(display-mode: fullscreen)");
233
+ if (fs?.matches) return { installed: true, source: "match_media" };
234
+ if (navigator.standalone === true) return { installed: true, source: "navigator_standalone" };
235
+ return { installed: false, source: null };
236
+ }
237
+ function track(event, props) {
238
+ if (typeof window === "undefined") return;
239
+ window.posthog?.capture?.(event, props);
240
+ }
241
+ function pickVariant(state) {
242
+ if (state.isInstalled) return "none";
243
+ switch (state.platform) {
244
+ case "android":
245
+ return state.isInstallable ? "android-native" : "android-manual";
246
+ case "ios-safari":
247
+ return "ios-safari";
248
+ case "ios-other":
249
+ return "ios-other";
250
+ case "in-app":
251
+ return "in-app";
252
+ case "desktop":
253
+ return state.isInstallable ? "desktop" : "none";
254
+ default:
255
+ return "none";
256
+ }
257
+ }
258
+ function safeStorage() {
259
+ if (typeof localStorage === "undefined") return null;
260
+ try {
261
+ localStorage.setItem("__install_probe", "1");
262
+ localStorage.removeItem("__install_probe");
263
+ return localStorage;
264
+ } catch {
265
+ return null;
266
+ }
267
+ }
268
+ function readPermanentDismiss(slug) {
269
+ const storage = safeStorage();
270
+ if (!storage) return { dismissed: false, dismissedAt: null };
271
+ const raw = storage.getItem(storageKey.dismissedAt(slug));
272
+ if (!raw) return { dismissed: false, dismissedAt: null };
273
+ const parsed = Date.parse(raw);
274
+ if (Number.isNaN(parsed)) return { dismissed: false, dismissedAt: null };
275
+ const daysAgo = (Date.now() - parsed) / (1e3 * 60 * 60 * 24);
276
+ return { dismissed: daysAgo < PERMANENT_DISMISS_REPROMPT_DAYS, dismissedAt: raw };
277
+ }
278
+ function readInstalledMarker(slug) {
279
+ const storage = safeStorage();
280
+ if (!storage) return false;
281
+ return storage.getItem(storageKey.installedAt(slug)) !== null;
282
+ }
283
+ function readSkipCount(slug) {
284
+ const storage = safeStorage();
285
+ if (!storage) return 0;
286
+ const raw = storage.getItem(storageKey.skipCount(slug));
287
+ const n = raw ? Number.parseInt(raw, 10) : 0;
288
+ return Number.isFinite(n) && n >= 0 ? n : 0;
289
+ }
290
+ function readSessionSkip(slug) {
291
+ if (typeof sessionStorage === "undefined") return false;
292
+ try {
293
+ return sessionStorage.getItem(storageKey.sessionSkip(slug)) === "true";
294
+ } catch {
295
+ return false;
296
+ }
297
+ }
298
+ function useInstallPrompt(slug) {
299
+ const ua = typeof navigator !== "undefined" && typeof navigator.userAgent === "string" ? navigator.userAgent : "";
300
+ const platform = detectPlatform(ua);
301
+ const iosBrowser = detectIOSBrowser(ua);
302
+ const androidBrowser = detectAndroidBrowser(ua);
303
+ const inAppApp = detectInAppApp(ua);
304
+ const [isInstallable, setIsInstallable] = useState3(() => {
305
+ if (typeof window === "undefined") return false;
306
+ return window.__pwaInstallPrompt != null;
307
+ });
308
+ const [isInstalled, setIsInstalled] = useState3(() => {
309
+ const { installed } = detectStandalone();
310
+ return installed || readInstalledMarker(slug);
311
+ });
312
+ const [isDismissedSession, setIsDismissedSession] = useState3(() => readSessionSkip(slug));
313
+ const [isDismissedPermanent, setIsDismissedPermanent] = useState3(() => readPermanentDismiss(slug).dismissed);
314
+ const [skipCount, setSkipCount] = useState3(() => readSkipCount(slug));
315
+ useEffect3(() => {
316
+ if (typeof window === "undefined") return;
317
+ if (window.__pwaInstallPrompt) {
318
+ setIsInstallable(true);
319
+ }
320
+ const onPrompt = (e) => {
321
+ e.preventDefault();
322
+ window.__pwaInstallPrompt = e;
323
+ setIsInstallable(true);
324
+ };
325
+ const onInstalled = () => {
326
+ setIsInstalled(true);
327
+ setIsInstallable(false);
328
+ window.__pwaInstallPrompt = null;
329
+ const storage = safeStorage();
330
+ if (storage) storage.setItem(storageKey.installedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
331
+ };
332
+ window.addEventListener("beforeinstallprompt", onPrompt);
333
+ window.addEventListener("appinstalled", onInstalled);
334
+ return () => {
335
+ window.removeEventListener("beforeinstallprompt", onPrompt);
336
+ window.removeEventListener("appinstalled", onInstalled);
337
+ };
338
+ }, [slug]);
339
+ useEffect3(() => {
340
+ if (typeof window === "undefined") return;
341
+ const mq = window.matchMedia?.("(display-mode: standalone)");
342
+ if (!mq) return;
343
+ const handler = (e) => {
344
+ if (e.matches) {
345
+ setIsInstalled(true);
346
+ track("pwa_install_standalone_detected", { slug, source: "match_media" });
347
+ }
348
+ };
349
+ mq.addEventListener?.("change", handler);
350
+ return () => mq.removeEventListener?.("change", handler);
351
+ }, [slug]);
352
+ const variant = pickVariant({
353
+ platform,
354
+ iosBrowser,
355
+ androidBrowser,
356
+ inAppApp,
357
+ isInstallable,
358
+ isInstalled,
359
+ isDismissedSession,
360
+ isDismissedPermanent,
361
+ skipCount
362
+ });
363
+ const promptInstall = useCallback2(async () => {
364
+ if (typeof window === "undefined") return false;
365
+ const prompt = window.__pwaInstallPrompt;
366
+ if (!prompt) return false;
367
+ track("pwa_install_prompt_clicked", { slug });
368
+ try {
369
+ await prompt.prompt();
370
+ const { outcome } = await prompt.userChoice;
371
+ track("pwa_install_prompt_outcome", { slug, outcome });
372
+ if (outcome === "accepted") {
373
+ setIsInstalled(true);
374
+ setIsInstallable(false);
375
+ window.__pwaInstallPrompt = null;
376
+ const storage = safeStorage();
377
+ if (storage) storage.setItem(storageKey.installedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
378
+ return true;
379
+ }
380
+ return false;
381
+ } catch {
382
+ return false;
383
+ }
384
+ }, [slug]);
385
+ const dismissSession = useCallback2(() => {
386
+ if (typeof sessionStorage !== "undefined") {
387
+ try {
388
+ sessionStorage.setItem(storageKey.sessionSkip(slug), "true");
389
+ } catch {
390
+ }
391
+ }
392
+ const storage = safeStorage();
393
+ const newCount = skipCount + 1;
394
+ if (storage) storage.setItem(storageKey.skipCount(slug), String(newCount));
395
+ setSkipCount(newCount);
396
+ setIsDismissedSession(true);
397
+ track("pwa_install_session_skip", { slug, platform, skip_count: newCount });
398
+ }, [slug, skipCount, platform]);
399
+ const dismissPermanent = useCallback2(() => {
400
+ const storage = safeStorage();
401
+ if (storage) storage.setItem(storageKey.dismissedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
402
+ setIsDismissedPermanent(true);
403
+ track("pwa_install_permanent_dismiss", { slug, platform, prior_skip_count: skipCount });
404
+ }, [slug, platform, skipCount]);
405
+ const showIOSOtherHelp = useCallback2(() => {
406
+ track("pwa_install_ios_other_help_shown", { slug });
407
+ }, [slug]);
408
+ const copyLink = useCallback2(async () => {
409
+ if (typeof navigator === "undefined" || typeof location === "undefined") return;
410
+ try {
411
+ await navigator.clipboard?.writeText?.(location.href);
412
+ } catch {
413
+ }
414
+ }, []);
415
+ const reset = useCallback2(() => {
416
+ const storage = safeStorage();
417
+ if (storage) {
418
+ storage.removeItem(storageKey.dismissedAt(slug));
419
+ storage.removeItem(storageKey.installedAt(slug));
420
+ storage.removeItem(storageKey.skipCount(slug));
421
+ }
422
+ if (typeof sessionStorage !== "undefined") {
423
+ try {
424
+ sessionStorage.removeItem(storageKey.sessionSkip(slug));
425
+ } catch {
426
+ }
427
+ }
428
+ setIsDismissedSession(false);
429
+ setIsDismissedPermanent(false);
430
+ setSkipCount(0);
431
+ }, [slug]);
432
+ return {
433
+ platform,
434
+ iosBrowser,
435
+ androidBrowser,
436
+ inAppApp,
437
+ isInstallable,
438
+ isInstalled,
439
+ isDismissedSession,
440
+ isDismissedPermanent,
441
+ skipCount,
442
+ variant,
443
+ promptInstall,
444
+ dismissSession,
445
+ dismissPermanent,
446
+ showIOSOtherHelp,
447
+ copyLink,
448
+ reset
449
+ };
450
+ }
451
+ function shouldBlockInstall(state, now = Date.now()) {
452
+ if (state.isInstalled) return false;
453
+ if (state.variant === "none") return false;
454
+ if (state.isDismissedSession) return false;
455
+ if (state.isDismissedPermanent) {
456
+ void now;
457
+ return false;
458
+ }
459
+ if (state.platform === "desktop" && !state.isInstallable) return false;
460
+ if (state.platform === "unknown") return false;
461
+ return true;
462
+ }
463
+ function shouldShowPermanentOption(state) {
464
+ return state.skipCount >= SESSION_SKIPS_BEFORE_PERMANENT_OPTION;
465
+ }
466
+
467
+ // src/components/InstallGate/copy.ts
468
+ var INSTALL_COPY = {
469
+ android: {
470
+ native: {
471
+ title: "Instale no seu celular",
472
+ subtitle: "Acesso r\xE1pido, sem precisar do navegador",
473
+ cta: "Baixar",
474
+ skip: "Continuar no navegador",
475
+ skipPermanent: "N\xE3o me pergunte mais"
476
+ },
477
+ manual: {
478
+ title: "Instale em 2 toques",
479
+ subtitle: 'Toque no menu do navegador e escolha "Instalar aplicativo"',
480
+ step1: "Toque no menu do navegador",
481
+ step2: 'Escolha "Instalar aplicativo"',
482
+ cta: "Entendi",
483
+ skip: "Continuar no navegador",
484
+ skipPermanent: "N\xE3o me pergunte mais"
485
+ }
486
+ },
487
+ iosSafari: {
488
+ title: "Adicione \xE0 sua Tela de In\xEDcio",
489
+ subtitle: "Siga os 3 passos",
490
+ step1: {
491
+ title: "Toque em Compartilhar",
492
+ subtitle: "Na barra inferior do Safari"
493
+ },
494
+ step2: {
495
+ title: 'Role e toque em "Adicionar \xE0 Tela de In\xEDcio"',
496
+ iconLabel: "Adicionar \xE0 Tela de In\xEDcio"
497
+ },
498
+ step3: {
499
+ title: 'Toque em "Adicionar" pra confirmar',
500
+ buttonLabel: "Adicionar"
501
+ },
502
+ skip: "Continuar no Safari",
503
+ skipPermanent: "N\xE3o me pergunte mais"
504
+ },
505
+ iosOther: {
506
+ title: "Abra no Safari pra instalar",
507
+ subtitle: "No Chrome/Firefox/Edge do iPhone n\xE3o d\xE1 pra instalar PWA. Abra o link no Safari.",
508
+ ctaPrimary: "Abrir no Safari",
509
+ ctaSecondary: "Copiar link",
510
+ copiedToast: "Link copiado. Cole no Safari.",
511
+ skip: "Continuar mesmo assim",
512
+ skipPermanent: "N\xE3o me pergunte mais",
513
+ help: {
514
+ step1: {
515
+ title: "Toque em \u22EF ou no bot\xE3o compartilhar",
516
+ subtitle: "Na barra inferior ou superior do navegador"
517
+ },
518
+ step2: {
519
+ title: 'Escolha "Abrir no Safari"',
520
+ subtitle: "O app vai abrir direto no Safari"
521
+ }
522
+ }
523
+ },
524
+ inApp: {
525
+ instagram: {
526
+ title: "Pra instalar, abra fora do Instagram",
527
+ step1: "Toque em \u22EF (canto superior direito)",
528
+ step2: 'Escolha "Abrir no navegador externo"'
529
+ },
530
+ facebook: {
531
+ title: "Pra instalar, abra fora do Facebook",
532
+ step1: "Toque em \u22EE",
533
+ step2: 'Escolha "Abrir no navegador"'
534
+ },
535
+ tiktok: {
536
+ title: "Pra instalar, abra fora do TikTok",
537
+ step1: "Toque em \u22EF",
538
+ step2: 'Escolha "Abrir no Safari" (iOS) ou "Abrir no Chrome" (Android)'
539
+ },
540
+ whatsapp: {
541
+ title: "Pra instalar, abra fora do WhatsApp",
542
+ step1: "Toque longo no link",
543
+ step2: 'Escolha "Abrir no navegador"'
544
+ },
545
+ twitter: {
546
+ title: "Pra instalar, abra fora do Twitter",
547
+ step1: "Toque em \u22EF",
548
+ step2: 'Escolha "Abrir no navegador"'
549
+ },
550
+ linkedin: {
551
+ title: "Pra instalar, abra fora do LinkedIn",
552
+ step1: "Toque em \u22EF",
553
+ step2: 'Escolha "Abrir no navegador"'
554
+ },
555
+ telegram: {
556
+ title: "Pra instalar, abra fora do Telegram",
557
+ step1: "Toque em \u22EE",
558
+ step2: 'Escolha "Abrir no navegador"'
559
+ },
560
+ line: {
561
+ title: "Pra instalar, abra fora do LINE",
562
+ step1: "Toque em \u22EF",
563
+ step2: 'Escolha "Abrir no navegador"'
564
+ },
565
+ snapchat: {
566
+ title: "Pra instalar, abra fora do Snapchat",
567
+ step1: "Mantenha pressionado o link",
568
+ step2: 'Escolha "Abrir no navegador"'
569
+ },
570
+ pinterest: {
571
+ title: "Pra instalar, abra fora do Pinterest",
572
+ step1: "Toque em \u22EF",
573
+ step2: 'Escolha "Abrir no navegador"'
574
+ },
575
+ wechat: {
576
+ title: "Pra instalar, abra fora do WeChat",
577
+ step1: "Toque em \u22EF",
578
+ step2: 'Escolha "Abrir no navegador"'
579
+ },
580
+ other: {
581
+ title: "Abra no navegador do celular",
582
+ step1: "Toque no menu do app atual",
583
+ step2: 'Escolha "Abrir no Chrome" ou "Abrir no Safari"'
584
+ },
585
+ copy: "Copiar link",
586
+ copiedToast: "Link copiado. Cole no Chrome/Safari.",
587
+ skip: "Continuar aqui mesmo",
588
+ skipPermanent: "N\xE3o me pergunte mais"
589
+ },
590
+ desktop: {
591
+ title: "Instale no computador",
592
+ subtitle: "Acesso r\xE1pido",
593
+ cta: "Baixar",
594
+ close: "Fechar"
595
+ }
596
+ };
597
+
598
+ // src/components/InstallGate/InstallSplash.tsx
599
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
600
+ function InstallSplash({ children, title, subtitle }) {
601
+ const { name, theme } = useTemplateConfig();
602
+ const iconUrl = theme.icon_url || theme.logo_url || null;
603
+ return /* @__PURE__ */ jsx7(
604
+ "div",
605
+ {
606
+ role: "dialog",
607
+ "aria-modal": "true",
608
+ "aria-labelledby": "install-splash-title",
609
+ "aria-describedby": subtitle ? "install-splash-subtitle" : void 0,
610
+ style: overlayStyle,
611
+ children: /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
612
+ /* @__PURE__ */ jsx7("div", { style: { display: "flex", justifyContent: "center", marginBottom: 16 }, children: iconUrl ? /* @__PURE__ */ jsx7(
613
+ "img",
614
+ {
615
+ src: iconUrl,
616
+ alt: `\xCDcone de ${name}`,
617
+ style: { width: 80, height: 80, borderRadius: 20, objectFit: "cover" }
618
+ }
619
+ ) : /* @__PURE__ */ jsx7(
620
+ "div",
621
+ {
622
+ style: {
623
+ width: 80,
624
+ height: 80,
625
+ borderRadius: 20,
626
+ background: "var(--hook-color-primary)",
627
+ color: "#fff",
628
+ display: "flex",
629
+ alignItems: "center",
630
+ justifyContent: "center",
631
+ fontSize: 36,
632
+ fontWeight: 700
633
+ },
634
+ children: name.charAt(0).toUpperCase()
635
+ }
636
+ ) }),
637
+ /* @__PURE__ */ jsx7("h1", { id: "install-splash-title", style: titleStyle, children: title }),
638
+ subtitle && /* @__PURE__ */ jsx7("p", { id: "install-splash-subtitle", style: subtitleStyle, children: subtitle }),
639
+ /* @__PURE__ */ jsx7("div", { style: { marginTop: 24 }, children }),
640
+ /* @__PURE__ */ jsx7("p", { style: footerStyle, children: "por Hook" })
641
+ ] })
642
+ }
643
+ );
644
+ }
645
+ var primaryButtonStyle = {
646
+ width: "100%",
647
+ padding: "14px 20px",
648
+ background: "var(--hook-color-primary)",
649
+ color: "#fff",
650
+ border: "none",
651
+ borderRadius: 999,
652
+ fontSize: 17,
653
+ fontWeight: 600,
654
+ cursor: "pointer",
655
+ marginBottom: 12
656
+ };
657
+ var secondaryButtonStyle = {
658
+ width: "100%",
659
+ padding: "12px 20px",
660
+ background: "transparent",
661
+ color: "var(--hook-color-primary)",
662
+ border: "1px solid var(--hook-color-primary)",
663
+ borderRadius: 999,
664
+ fontSize: 15,
665
+ fontWeight: 500,
666
+ cursor: "pointer",
667
+ marginBottom: 12
668
+ };
669
+ var skipLinkStyle = {
670
+ display: "block",
671
+ width: "100%",
672
+ padding: "10px",
673
+ marginTop: 8,
674
+ background: "transparent",
675
+ color: "#555",
676
+ border: "none",
677
+ fontSize: 14,
678
+ textDecoration: "underline",
679
+ cursor: "pointer",
680
+ textAlign: "center"
681
+ };
682
+ var skipPermanentLinkStyle = {
683
+ ...skipLinkStyle,
684
+ color: "#999",
685
+ fontSize: 13,
686
+ marginTop: 4
687
+ };
688
+ var overlayStyle = {
689
+ position: "fixed",
690
+ inset: 0,
691
+ background: "var(--hook-color-background, #fafafa)",
692
+ zIndex: 1e4,
693
+ display: "flex",
694
+ alignItems: "center",
695
+ justifyContent: "center",
696
+ padding: 20,
697
+ overflow: "auto"
698
+ };
699
+ var cardStyle = {
700
+ width: "100%",
701
+ maxWidth: 420,
702
+ padding: 24,
703
+ textAlign: "center"
704
+ };
705
+ var titleStyle = {
706
+ fontSize: 24,
707
+ fontWeight: 700,
708
+ lineHeight: 1.2,
709
+ margin: "0 0 8px 0",
710
+ color: "#111"
711
+ };
712
+ var subtitleStyle = {
713
+ fontSize: 15,
714
+ lineHeight: 1.4,
715
+ color: "#555",
716
+ margin: 0
717
+ };
718
+ var footerStyle = {
719
+ fontSize: 11,
720
+ color: "#aaa",
721
+ marginTop: 32,
722
+ letterSpacing: 0.5
723
+ };
724
+
725
+ // src/components/InstallGate/icons.tsx
726
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
727
+ var defaultSvgProps = (size) => ({
728
+ width: size,
729
+ height: size,
730
+ viewBox: "0 0 24 24",
731
+ fill: "none",
732
+ stroke: "currentColor",
733
+ strokeWidth: 2,
734
+ strokeLinecap: "round",
735
+ strokeLinejoin: "round"
736
+ });
737
+ function ShareIconIOS({ size = 24, style, className }) {
738
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
739
+ /* @__PURE__ */ jsx8("path", { d: "M12 2L12 15" }),
740
+ /* @__PURE__ */ jsx8("path", { d: "M8 6L12 2L16 6" }),
741
+ /* @__PURE__ */ jsx8("path", { d: "M4 11v9a2 2 0 002 2h12a2 2 0 002-2v-9" })
742
+ ] });
743
+ }
744
+ function MenuDotsVerticalIcon({ size = 24, style, className }) {
745
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
746
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "5", r: "1.5" }),
747
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "1.5" }),
748
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "19", r: "1.5" })
749
+ ] });
750
+ }
751
+ function MenuDotsHorizontalIcon({ size = 24, style, className }) {
752
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
753
+ /* @__PURE__ */ jsx8("circle", { cx: "5", cy: "12", r: "1.5" }),
754
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "1.5" }),
755
+ /* @__PURE__ */ jsx8("circle", { cx: "19", cy: "12", r: "1.5" })
756
+ ] });
757
+ }
758
+ function SquarePlusIcon({ size = 24, style, className }) {
759
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
760
+ /* @__PURE__ */ jsx8("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
761
+ /* @__PURE__ */ jsx8("path", { d: "M12 8v8" }),
762
+ /* @__PURE__ */ jsx8("path", { d: "M8 12h8" })
763
+ ] });
764
+ }
765
+ function DownloadIcon({ size = 24, style, className }) {
766
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
767
+ /* @__PURE__ */ jsx8("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }),
768
+ /* @__PURE__ */ jsx8("polyline", { points: "7 10 12 15 17 10" }),
769
+ /* @__PURE__ */ jsx8("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
770
+ ] });
771
+ }
772
+ function ExternalLinkIcon({ size = 24, style, className }) {
773
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
774
+ /* @__PURE__ */ jsx8("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
775
+ /* @__PURE__ */ jsx8("polyline", { points: "15 3 21 3 21 9" }),
776
+ /* @__PURE__ */ jsx8("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
777
+ ] });
778
+ }
779
+ function XIcon({ size = 20, style, className }) {
780
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
781
+ /* @__PURE__ */ jsx8("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
782
+ /* @__PURE__ */ jsx8("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
783
+ ] });
784
+ }
785
+
786
+ // src/components/InstallGate/variants/AndroidNativeVariant.tsx
787
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
788
+ function AndroidNativeVariant({
789
+ state,
790
+ actions
791
+ }) {
792
+ const copy = INSTALL_COPY.android.native;
793
+ const showPermanent = shouldShowPermanentOption(state);
794
+ return /* @__PURE__ */ jsxs3(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
795
+ /* @__PURE__ */ jsxs3(
796
+ "button",
797
+ {
798
+ "data-testid": "install-prompt-cta-android-native",
799
+ type: "button",
800
+ onClick: () => void actions.promptInstall(),
801
+ style: { ...primaryButtonStyle, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8 },
802
+ children: [
803
+ /* @__PURE__ */ jsx9(DownloadIcon, { size: 18 }),
804
+ copy.cta
805
+ ]
806
+ }
807
+ ),
808
+ /* @__PURE__ */ jsx9(
809
+ "button",
810
+ {
811
+ "data-testid": "install-prompt-skip-session",
812
+ type: "button",
813
+ onClick: actions.dismissSession,
814
+ style: skipLinkStyle,
815
+ children: copy.skip
816
+ }
817
+ ),
818
+ showPermanent && /* @__PURE__ */ jsx9(
819
+ "button",
820
+ {
821
+ "data-testid": "install-prompt-skip-permanent",
822
+ type: "button",
823
+ onClick: actions.dismissPermanent,
824
+ style: skipPermanentLinkStyle,
825
+ children: copy.skipPermanent
826
+ }
827
+ )
828
+ ] });
829
+ }
830
+
831
+ // src/components/InstallGate/variants/AndroidManualVariant.tsx
832
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
833
+ function AndroidManualVariant({
834
+ state,
835
+ actions
836
+ }) {
837
+ const copy = INSTALL_COPY.android.manual;
838
+ const showPermanent = shouldShowPermanentOption(state);
839
+ return /* @__PURE__ */ jsxs4(InstallSplash, { title: copy.title, children: [
840
+ /* @__PURE__ */ jsx10(Step, { n: 1, icon: /* @__PURE__ */ jsx10(MenuDotsVerticalIcon, { size: 20 }), children: copy.step1 }),
841
+ /* @__PURE__ */ jsx10(Step, { n: 2, icon: /* @__PURE__ */ jsx10(DownloadIcon, { size: 18 }), children: copy.step2 }),
842
+ /* @__PURE__ */ jsx10(
843
+ "button",
844
+ {
845
+ "data-testid": "install-prompt-cta-android-manual",
846
+ type: "button",
847
+ onClick: actions.dismissSession,
848
+ style: primaryButtonStyle,
849
+ children: copy.cta
850
+ }
851
+ ),
852
+ /* @__PURE__ */ jsx10(
853
+ "button",
854
+ {
855
+ "data-testid": "install-prompt-skip-session",
856
+ type: "button",
857
+ onClick: actions.dismissSession,
858
+ style: skipLinkStyle,
859
+ children: copy.skip
860
+ }
861
+ ),
862
+ showPermanent && /* @__PURE__ */ jsx10(
863
+ "button",
864
+ {
865
+ "data-testid": "install-prompt-skip-permanent",
866
+ type: "button",
867
+ onClick: actions.dismissPermanent,
868
+ style: skipPermanentLinkStyle,
869
+ children: copy.skipPermanent
870
+ }
871
+ )
872
+ ] });
873
+ }
874
+ function Step({ n, icon, children }) {
875
+ return /* @__PURE__ */ jsxs4(
876
+ "div",
877
+ {
878
+ style: {
879
+ display: "flex",
880
+ alignItems: "center",
881
+ gap: 12,
882
+ padding: "12px 14px",
883
+ background: "#f5f5f7",
884
+ borderRadius: 12,
885
+ marginBottom: 10,
886
+ textAlign: "left"
887
+ },
888
+ children: [
889
+ /* @__PURE__ */ jsx10(
890
+ "div",
891
+ {
892
+ style: {
893
+ width: 28,
894
+ height: 28,
895
+ borderRadius: "50%",
896
+ background: "var(--hook-color-primary)",
897
+ color: "#fff",
898
+ display: "flex",
899
+ alignItems: "center",
900
+ justifyContent: "center",
901
+ fontSize: 14,
902
+ fontWeight: 700,
903
+ flexShrink: 0
904
+ },
905
+ children: n
906
+ }
907
+ ),
908
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, fontSize: 15, color: "#333" }, children }),
909
+ /* @__PURE__ */ jsx10("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
910
+ ]
911
+ }
912
+ );
913
+ }
914
+
915
+ // src/components/InstallGate/Step.tsx
916
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
917
+ function Step2({
918
+ n,
919
+ title,
920
+ subtitle,
921
+ visual
922
+ }) {
923
+ return /* @__PURE__ */ jsxs5(
924
+ "div",
925
+ {
926
+ style: {
927
+ display: "flex",
928
+ alignItems: "flex-start",
929
+ gap: 12,
930
+ marginBottom: 16,
931
+ textAlign: "left"
932
+ },
933
+ children: [
934
+ /* @__PURE__ */ jsx11(
935
+ "div",
936
+ {
937
+ style: {
938
+ width: 32,
939
+ height: 32,
940
+ borderRadius: 10,
941
+ background: "var(--hook-color-primary)",
942
+ color: "#fff",
943
+ display: "flex",
944
+ alignItems: "center",
945
+ justifyContent: "center",
946
+ fontSize: 15,
947
+ fontWeight: 700,
948
+ flexShrink: 0
949
+ },
950
+ children: n
951
+ }
952
+ ),
953
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
954
+ /* @__PURE__ */ jsx11("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
955
+ subtitle && /* @__PURE__ */ jsx11("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
956
+ visual
957
+ ] })
958
+ ]
959
+ }
960
+ );
961
+ }
962
+
963
+ // src/components/InstallGate/variants/IOSafariVariant.tsx
964
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
965
+ function IOSafariVariant({
966
+ state,
967
+ actions
968
+ }) {
969
+ const copy = INSTALL_COPY.iosSafari;
970
+ const showPermanent = shouldShowPermanentOption(state);
971
+ return /* @__PURE__ */ jsxs6(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
972
+ /* @__PURE__ */ jsx12(
973
+ Step2,
974
+ {
975
+ n: 1,
976
+ title: copy.step1.title,
977
+ subtitle: copy.step1.subtitle,
978
+ visual: /* @__PURE__ */ jsx12(
979
+ "div",
980
+ {
981
+ style: {
982
+ display: "flex",
983
+ justifyContent: "center",
984
+ alignItems: "center",
985
+ background: "#f5f5f7",
986
+ borderRadius: 12,
987
+ padding: "12px 0",
988
+ marginTop: 8
989
+ },
990
+ children: /* @__PURE__ */ jsx12(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
991
+ }
992
+ )
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx12(
996
+ Step2,
997
+ {
998
+ n: 2,
999
+ title: copy.step2.title,
1000
+ visual: /* @__PURE__ */ jsxs6(
1001
+ "div",
1002
+ {
1003
+ style: {
1004
+ display: "flex",
1005
+ alignItems: "center",
1006
+ gap: 10,
1007
+ background: "#f5f5f7",
1008
+ borderRadius: 12,
1009
+ padding: "12px 14px",
1010
+ marginTop: 8
1011
+ },
1012
+ children: [
1013
+ /* @__PURE__ */ jsx12(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
1014
+ /* @__PURE__ */ jsx12("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
1015
+ ]
1016
+ }
1017
+ )
1018
+ }
1019
+ ),
1020
+ /* @__PURE__ */ jsx12(
1021
+ Step2,
1022
+ {
1023
+ n: 3,
1024
+ title: copy.step3.title,
1025
+ visual: /* @__PURE__ */ jsx12(
1026
+ "div",
1027
+ {
1028
+ style: {
1029
+ display: "flex",
1030
+ justifyContent: "flex-end",
1031
+ background: "#f5f5f7",
1032
+ borderRadius: 12,
1033
+ padding: "10px 14px",
1034
+ marginTop: 8
1035
+ },
1036
+ children: /* @__PURE__ */ jsx12(
1037
+ "span",
1038
+ {
1039
+ style: {
1040
+ color: "var(--hook-color-primary)",
1041
+ fontSize: 15,
1042
+ fontWeight: 600
1043
+ },
1044
+ children: copy.step3.buttonLabel
1045
+ }
1046
+ )
1047
+ }
1048
+ )
1049
+ }
1050
+ ),
1051
+ /* @__PURE__ */ jsx12(
1052
+ "button",
1053
+ {
1054
+ "data-testid": "install-prompt-skip-session",
1055
+ type: "button",
1056
+ onClick: actions.dismissSession,
1057
+ style: { ...skipLinkStyle, marginTop: 16 },
1058
+ children: copy.skip
1059
+ }
1060
+ ),
1061
+ showPermanent && /* @__PURE__ */ jsx12(
1062
+ "button",
1063
+ {
1064
+ "data-testid": "install-prompt-skip-permanent",
1065
+ type: "button",
1066
+ onClick: actions.dismissPermanent,
1067
+ style: skipPermanentLinkStyle,
1068
+ children: copy.skipPermanent
1069
+ }
1070
+ )
1071
+ ] });
1072
+ }
1073
+
1074
+ // src/components/InstallGate/variants/IOSOtherVariant.tsx
1075
+ import { useState as useState4 } from "react";
1076
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1077
+ function IOSOtherVariant({
1078
+ state,
1079
+ actions
1080
+ }) {
1081
+ const copy = INSTALL_COPY.iosOther;
1082
+ const showPermanent = shouldShowPermanentOption(state);
1083
+ const [copied, setCopied] = useState4(false);
1084
+ const [helpOpen, setHelpOpen] = useState4(false);
1085
+ const handleCopy = async () => {
1086
+ await actions.copyLink();
1087
+ setCopied(true);
1088
+ setTimeout(() => setCopied(false), 2e3);
1089
+ };
1090
+ const handleShowHelp = () => {
1091
+ if (!helpOpen) actions.showIOSOtherHelp();
1092
+ setHelpOpen(true);
1093
+ };
1094
+ return /* @__PURE__ */ jsxs7(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
1095
+ /* @__PURE__ */ jsx13(
1096
+ "button",
1097
+ {
1098
+ "data-testid": "install-prompt-cta-ios-other-primary",
1099
+ type: "button",
1100
+ onClick: handleShowHelp,
1101
+ "aria-expanded": helpOpen,
1102
+ style: primaryButtonStyle,
1103
+ children: copy.ctaPrimary
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsx13(
1107
+ "button",
1108
+ {
1109
+ "data-testid": "install-prompt-cta-ios-other-secondary",
1110
+ type: "button",
1111
+ onClick: () => void handleCopy(),
1112
+ style: secondaryButtonStyle,
1113
+ children: copied ? copy.copiedToast : copy.ctaSecondary
1114
+ }
1115
+ ),
1116
+ helpOpen && /* @__PURE__ */ jsxs7(
1117
+ "div",
1118
+ {
1119
+ "data-testid": "install-prompt-ios-other-help",
1120
+ style: { marginTop: 20, textAlign: "left" },
1121
+ children: [
1122
+ /* @__PURE__ */ jsx13(
1123
+ Step2,
1124
+ {
1125
+ n: 1,
1126
+ title: copy.help.step1.title,
1127
+ subtitle: copy.help.step1.subtitle,
1128
+ visual: /* @__PURE__ */ jsx13(
1129
+ "div",
1130
+ {
1131
+ style: {
1132
+ display: "flex",
1133
+ justifyContent: "center",
1134
+ alignItems: "center",
1135
+ background: "#f5f5f7",
1136
+ borderRadius: 12,
1137
+ padding: "12px 0",
1138
+ marginTop: 8
1139
+ },
1140
+ children: /* @__PURE__ */ jsx13(
1141
+ MenuDotsHorizontalIcon,
1142
+ {
1143
+ size: 28,
1144
+ style: { color: "var(--hook-color-primary)" }
1145
+ }
1146
+ )
1147
+ }
1148
+ )
1149
+ }
1150
+ ),
1151
+ /* @__PURE__ */ jsx13(
1152
+ Step2,
1153
+ {
1154
+ n: 2,
1155
+ title: copy.help.step2.title,
1156
+ subtitle: copy.help.step2.subtitle,
1157
+ visual: /* @__PURE__ */ jsxs7(
1158
+ "div",
1159
+ {
1160
+ style: {
1161
+ display: "flex",
1162
+ alignItems: "center",
1163
+ gap: 10,
1164
+ background: "#f5f5f7",
1165
+ borderRadius: 12,
1166
+ padding: "12px 14px",
1167
+ marginTop: 8
1168
+ },
1169
+ children: [
1170
+ /* @__PURE__ */ jsx13(ExternalLinkIcon, { size: 22, style: { color: "#555" } }),
1171
+ /* @__PURE__ */ jsx13("span", { style: { fontSize: 14, color: "#333" }, children: "Abrir no Safari" })
1172
+ ]
1173
+ }
1174
+ )
1175
+ }
1176
+ )
1177
+ ]
1178
+ }
1179
+ ),
1180
+ /* @__PURE__ */ jsx13(
1181
+ "button",
1182
+ {
1183
+ "data-testid": "install-prompt-skip-session",
1184
+ type: "button",
1185
+ onClick: actions.dismissSession,
1186
+ style: skipLinkStyle,
1187
+ children: copy.skip
1188
+ }
1189
+ ),
1190
+ showPermanent && /* @__PURE__ */ jsx13(
1191
+ "button",
1192
+ {
1193
+ "data-testid": "install-prompt-skip-permanent",
1194
+ type: "button",
1195
+ onClick: actions.dismissPermanent,
1196
+ style: skipPermanentLinkStyle,
1197
+ children: copy.skipPermanent
1198
+ }
1199
+ )
1200
+ ] });
1201
+ }
1202
+
1203
+ // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1204
+ import { useState as useState5 } from "react";
1205
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1206
+ function InAppBrowserVariant({
1207
+ state,
1208
+ actions
1209
+ }) {
1210
+ const app = state.inAppApp ?? "other";
1211
+ const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1212
+ const copy = INSTALL_COPY.inApp;
1213
+ const showPermanent = shouldShowPermanentOption(state);
1214
+ const [copied, setCopied] = useState5(false);
1215
+ const handleCopy = async () => {
1216
+ await actions.copyLink();
1217
+ setCopied(true);
1218
+ setTimeout(() => setCopied(false), 2e3);
1219
+ };
1220
+ const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
1221
+ return /* @__PURE__ */ jsxs8(InstallSplash, { title: appCopy.title, children: [
1222
+ /* @__PURE__ */ jsx14(Step3, { n: 1, icon: /* @__PURE__ */ jsx14(DotsIcon, { size: 20 }), children: appCopy.step1 }),
1223
+ /* @__PURE__ */ jsx14(Step3, { n: 2, icon: /* @__PURE__ */ jsx14(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
1224
+ /* @__PURE__ */ jsx14(
1225
+ "button",
1226
+ {
1227
+ "data-testid": "install-prompt-cta-inapp-copy",
1228
+ type: "button",
1229
+ onClick: () => void handleCopy(),
1230
+ style: { ...primaryButtonStyle, marginTop: 8 },
1231
+ children: copied ? copy.copiedToast : copy.copy
1232
+ }
1233
+ ),
1234
+ /* @__PURE__ */ jsx14(
1235
+ "button",
1236
+ {
1237
+ "data-testid": "install-prompt-skip-session",
1238
+ type: "button",
1239
+ onClick: actions.dismissSession,
1240
+ style: skipLinkStyle,
1241
+ children: copy.skip
1242
+ }
1243
+ ),
1244
+ showPermanent && /* @__PURE__ */ jsx14(
1245
+ "button",
1246
+ {
1247
+ "data-testid": "install-prompt-skip-permanent",
1248
+ type: "button",
1249
+ onClick: actions.dismissPermanent,
1250
+ style: skipPermanentLinkStyle,
1251
+ children: copy.skipPermanent
1252
+ }
1253
+ )
1254
+ ] });
1255
+ }
1256
+ function Step3({
1257
+ n,
1258
+ icon,
1259
+ children
1260
+ }) {
1261
+ return /* @__PURE__ */ jsxs8(
1262
+ "div",
1263
+ {
1264
+ style: {
1265
+ display: "flex",
1266
+ alignItems: "center",
1267
+ gap: 12,
1268
+ padding: "12px 14px",
1269
+ background: "#f5f5f7",
1270
+ borderRadius: 12,
1271
+ marginBottom: 10,
1272
+ textAlign: "left"
1273
+ },
1274
+ children: [
1275
+ /* @__PURE__ */ jsx14(
1276
+ "div",
1277
+ {
1278
+ style: {
1279
+ width: 28,
1280
+ height: 28,
1281
+ borderRadius: "50%",
1282
+ background: "var(--hook-color-primary)",
1283
+ color: "#fff",
1284
+ display: "flex",
1285
+ alignItems: "center",
1286
+ justifyContent: "center",
1287
+ fontSize: 14,
1288
+ fontWeight: 700,
1289
+ flexShrink: 0
1290
+ },
1291
+ children: n
1292
+ }
1293
+ ),
1294
+ /* @__PURE__ */ jsx14("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
1295
+ /* @__PURE__ */ jsx14("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
1296
+ ]
1297
+ }
1298
+ );
1299
+ }
1300
+
1301
+ // src/components/InstallGate/variants/DesktopVariant.tsx
1302
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1303
+ function DesktopVariant({
1304
+ state,
1305
+ actions
1306
+ }) {
1307
+ const { name, theme } = useTemplateConfig();
1308
+ const copy = INSTALL_COPY.desktop;
1309
+ const iconUrl = theme.icon_url || theme.logo_url || null;
1310
+ if (!state.isInstallable) return null;
1311
+ return /* @__PURE__ */ jsxs9(
1312
+ "div",
1313
+ {
1314
+ role: "complementary",
1315
+ "aria-label": copy.title,
1316
+ style: bannerStyle,
1317
+ children: [
1318
+ iconUrl ? /* @__PURE__ */ jsx15(
1319
+ "img",
1320
+ {
1321
+ src: iconUrl,
1322
+ alt: "",
1323
+ style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
1324
+ }
1325
+ ) : /* @__PURE__ */ jsx15(
1326
+ "div",
1327
+ {
1328
+ style: {
1329
+ width: 40,
1330
+ height: 40,
1331
+ borderRadius: 10,
1332
+ background: "var(--hook-color-primary)",
1333
+ color: "#fff",
1334
+ display: "flex",
1335
+ alignItems: "center",
1336
+ justifyContent: "center",
1337
+ fontSize: 18,
1338
+ fontWeight: 700,
1339
+ flexShrink: 0
1340
+ },
1341
+ children: name.charAt(0).toUpperCase()
1342
+ }
1343
+ ),
1344
+ /* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
1345
+ /* @__PURE__ */ jsx15("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
1346
+ /* @__PURE__ */ jsx15("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
1347
+ ] }),
1348
+ /* @__PURE__ */ jsxs9(
1349
+ "button",
1350
+ {
1351
+ "data-testid": "install-prompt-cta-desktop",
1352
+ type: "button",
1353
+ onClick: () => void actions.promptInstall(),
1354
+ style: {
1355
+ padding: "8px 14px",
1356
+ background: "var(--hook-color-primary)",
1357
+ color: "#fff",
1358
+ border: "none",
1359
+ borderRadius: 999,
1360
+ fontSize: 13,
1361
+ fontWeight: 600,
1362
+ cursor: "pointer",
1363
+ display: "inline-flex",
1364
+ alignItems: "center",
1365
+ gap: 6,
1366
+ flexShrink: 0
1367
+ },
1368
+ children: [
1369
+ /* @__PURE__ */ jsx15(DownloadIcon, { size: 14 }),
1370
+ copy.cta
1371
+ ]
1372
+ }
1373
+ ),
1374
+ /* @__PURE__ */ jsx15(
1375
+ "button",
1376
+ {
1377
+ "data-testid": "install-prompt-desktop-close",
1378
+ type: "button",
1379
+ onClick: actions.dismissPermanent,
1380
+ "aria-label": copy.close,
1381
+ style: {
1382
+ background: "transparent",
1383
+ border: "none",
1384
+ cursor: "pointer",
1385
+ color: "#888",
1386
+ padding: 4,
1387
+ flexShrink: 0
1388
+ },
1389
+ children: /* @__PURE__ */ jsx15(XIcon, { size: 16 })
1390
+ }
1391
+ )
1392
+ ]
1393
+ }
1394
+ );
1395
+ }
1396
+ var bannerStyle = {
1397
+ position: "fixed",
1398
+ bottom: 24,
1399
+ right: 24,
1400
+ zIndex: 1e4,
1401
+ display: "flex",
1402
+ alignItems: "center",
1403
+ gap: 12,
1404
+ padding: "12px 16px",
1405
+ background: "#fff",
1406
+ border: "1px solid rgba(0,0,0,0.08)",
1407
+ borderRadius: 16,
1408
+ boxShadow: "0 10px 30px rgba(0,0,0,0.12)",
1409
+ maxWidth: 400
1410
+ };
1411
+
1412
+ // src/components/InstallGate/InstallGate.tsx
1413
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1414
+ function InstallGate({ children }) {
1415
+ const { slug, features_enabled } = useTemplateConfig();
1416
+ const enabled = features_enabled.includes("install_prompt");
1417
+ const installState = useInstallPrompt(slug);
1418
+ const shouldBlock = enabled && shouldBlockInstall(installState);
1419
+ const trackedRef = useRef(null);
1420
+ useEffect4(() => {
1421
+ if (!shouldBlock) return;
1422
+ if (typeof window === "undefined") return;
1423
+ const variantKey = `${slug}:${installState.variant}`;
1424
+ if (trackedRef.current === variantKey) return;
1425
+ trackedRef.current = variantKey;
1426
+ window.posthog?.capture?.("pwa_install_splash_shown", {
1427
+ slug,
1428
+ platform: installState.platform,
1429
+ browser: installState.iosBrowser ?? installState.androidBrowser ?? null,
1430
+ in_app_app: installState.inAppApp,
1431
+ variant: installState.variant
1432
+ });
1433
+ }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
1434
+ if (!enabled) return /* @__PURE__ */ jsx16(Fragment4, { children });
1435
+ if (installState.isInstalled) return /* @__PURE__ */ jsx16(Fragment4, { children });
1436
+ if (installState.variant === "desktop") {
1437
+ return /* @__PURE__ */ jsxs10(Fragment4, { children: [
1438
+ children,
1439
+ /* @__PURE__ */ jsx16(DesktopVariant, { state: installState, actions: installState })
1440
+ ] });
1441
+ }
1442
+ if (!shouldBlock) return /* @__PURE__ */ jsx16(Fragment4, { children });
1443
+ switch (installState.variant) {
1444
+ case "android-native":
1445
+ return /* @__PURE__ */ jsx16(AndroidNativeVariant, { state: installState, actions: installState });
1446
+ case "android-manual":
1447
+ return /* @__PURE__ */ jsx16(AndroidManualVariant, { state: installState, actions: installState });
1448
+ case "ios-safari":
1449
+ return /* @__PURE__ */ jsx16(IOSafariVariant, { state: installState, actions: installState });
1450
+ case "ios-other":
1451
+ return /* @__PURE__ */ jsx16(IOSOtherVariant, { state: installState, actions: installState });
1452
+ case "in-app":
1453
+ return /* @__PURE__ */ jsx16(InAppBrowserVariant, { state: installState, actions: installState });
1454
+ case "none":
1455
+ default:
1456
+ return /* @__PURE__ */ jsx16(Fragment4, { children });
1457
+ }
1458
+ }
1459
+
167
1460
  // src/defaults/ErrorBoundary.tsx
168
1461
  import { Component } from "react";
169
- import { Fragment as Fragment4, jsx as jsx7, jsxs } from "react/jsx-runtime";
1462
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
170
1463
  var ErrorBoundary = class extends Component {
171
1464
  state = { error: null };
172
1465
  static getDerivedStateFromError(error) {
@@ -177,17 +1470,17 @@ var ErrorBoundary = class extends Component {
177
1470
  }
178
1471
  render() {
179
1472
  if (this.state.error) {
180
- return this.props.fallback ?? /* @__PURE__ */ jsxs("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
181
- /* @__PURE__ */ jsx7("h2", { children: "Algo deu errado" }),
182
- /* @__PURE__ */ jsx7("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
1473
+ return this.props.fallback ?? /* @__PURE__ */ jsxs11("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
1474
+ /* @__PURE__ */ jsx17("h2", { children: "Algo deu errado" }),
1475
+ /* @__PURE__ */ jsx17("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
183
1476
  ] });
184
1477
  }
185
- return /* @__PURE__ */ jsx7(Fragment4, { children: this.props.children });
1478
+ return /* @__PURE__ */ jsx17(Fragment5, { children: this.props.children });
186
1479
  }
187
1480
  };
188
1481
 
189
1482
  // src/hooks/useLoginForm.ts
190
- import { useCallback as useCallback2, useMemo as useMemo2, useState as useState3 } from "react";
1483
+ import { useCallback as useCallback3, useMemo as useMemo2, useState as useState6 } from "react";
191
1484
  import { useHook as useHook4 } from "@hook-sdk/sdk";
192
1485
 
193
1486
  // src/errors.ts
@@ -224,10 +1517,10 @@ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
224
1517
  var MIN_PASSWORD = 8;
225
1518
  function useLoginForm() {
226
1519
  const { auth } = useHook4();
227
- const [email, setEmail] = useState3("");
228
- const [password, setPassword] = useState3("");
229
- const [submitting, setSubmitting] = useState3(false);
230
- const [error, setError] = useState3(null);
1520
+ const [email, setEmail] = useState6("");
1521
+ const [password, setPassword] = useState6("");
1522
+ const [submitting, setSubmitting] = useState6(false);
1523
+ const [error, setError] = useState6(null);
231
1524
  const emailError = useMemo2(() => {
232
1525
  if (email.length === 0) return null;
233
1526
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -239,7 +1532,7 @@ function useLoginForm() {
239
1532
  return null;
240
1533
  }, [password]);
241
1534
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && emailError === null && passwordError === null && !submitting;
242
- const submit = useCallback2(async () => {
1535
+ const submit = useCallback3(async () => {
243
1536
  if (!canSubmit) return false;
244
1537
  setSubmitting(true);
245
1538
  setError(null);
@@ -269,13 +1562,13 @@ function useLoginForm() {
269
1562
  }
270
1563
 
271
1564
  // src/internal/GoogleSignInButton.tsx
272
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
1565
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
273
1566
  function GoogleSignInButton({
274
1567
  onClick,
275
1568
  testId = "oauth-google",
276
1569
  label = "Continuar com Google"
277
1570
  }) {
278
- return /* @__PURE__ */ jsxs2(
1571
+ return /* @__PURE__ */ jsxs12(
279
1572
  "button",
280
1573
  {
281
1574
  "data-testid": testId,
@@ -298,36 +1591,36 @@ function GoogleSignInButton({
298
1591
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
299
1592
  },
300
1593
  children: [
301
- /* @__PURE__ */ jsx8(GoogleGlyph, {}),
1594
+ /* @__PURE__ */ jsx18(GoogleGlyph, {}),
302
1595
  label
303
1596
  ]
304
1597
  }
305
1598
  );
306
1599
  }
307
1600
  function GoogleGlyph() {
308
- return /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
309
- /* @__PURE__ */ jsx8(
1601
+ return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
1602
+ /* @__PURE__ */ jsx18(
310
1603
  "path",
311
1604
  {
312
1605
  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",
313
1606
  fill: "#4285F4"
314
1607
  }
315
1608
  ),
316
- /* @__PURE__ */ jsx8(
1609
+ /* @__PURE__ */ jsx18(
317
1610
  "path",
318
1611
  {
319
1612
  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",
320
1613
  fill: "#34A853"
321
1614
  }
322
1615
  ),
323
- /* @__PURE__ */ jsx8(
1616
+ /* @__PURE__ */ jsx18(
324
1617
  "path",
325
1618
  {
326
1619
  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",
327
1620
  fill: "#FBBC05"
328
1621
  }
329
1622
  ),
330
- /* @__PURE__ */ jsx8(
1623
+ /* @__PURE__ */ jsx18(
331
1624
  "path",
332
1625
  {
333
1626
  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",
@@ -338,8 +1631,8 @@ function GoogleGlyph() {
338
1631
  }
339
1632
 
340
1633
  // src/internal/OAuthErrorBanner.tsx
341
- import { useEffect as useEffect3, useState as useState4 } from "react";
342
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
1634
+ import { useEffect as useEffect5, useState as useState7 } from "react";
1635
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
343
1636
  var ERROR_MESSAGES = {
344
1637
  invalid_state: "Sess\xE3o expirou, tente de novo.",
345
1638
  access_denied: "Voc\xEA cancelou o login com Google.",
@@ -359,13 +1652,13 @@ function stripErrorFromUrl() {
359
1652
  window.history.replaceState({}, "", url.toString());
360
1653
  }
361
1654
  function OAuthErrorBanner() {
362
- const [code, setCode] = useState4(() => readErrorCode());
363
- useEffect3(() => {
1655
+ const [code, setCode] = useState7(() => readErrorCode());
1656
+ useEffect5(() => {
364
1657
  if (code !== null) stripErrorFromUrl();
365
1658
  }, [code]);
366
1659
  if (!code) return null;
367
1660
  const message = ERROR_MESSAGES[code] ?? "N\xE3o conseguimos conectar ao Google. Tente de novo.";
368
- return /* @__PURE__ */ jsxs3(
1661
+ return /* @__PURE__ */ jsxs13(
369
1662
  "div",
370
1663
  {
371
1664
  role: "alert",
@@ -380,7 +1673,7 @@ function OAuthErrorBanner() {
380
1673
  },
381
1674
  children: [
382
1675
  message,
383
- /* @__PURE__ */ jsx9(
1676
+ /* @__PURE__ */ jsx19(
384
1677
  "button",
385
1678
  {
386
1679
  type: "button",
@@ -405,16 +1698,16 @@ function OAuthErrorBanner() {
405
1698
  }
406
1699
 
407
1700
  // src/defaults/DefaultLoginScreen.tsx
408
- import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
1701
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
409
1702
  function DefaultLoginScreen({ onNavigate }) {
410
1703
  const { name } = useTemplateConfig();
411
1704
  const f = useLoginForm();
412
- return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
413
- /* @__PURE__ */ jsx10("h1", { style: { marginBottom: 8 }, children: name }),
414
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
415
- /* @__PURE__ */ jsx10(OAuthErrorBanner, {}),
416
- /* @__PURE__ */ jsx10(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
417
- /* @__PURE__ */ jsxs4(
1705
+ return /* @__PURE__ */ jsxs14("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1706
+ /* @__PURE__ */ jsx20("h1", { style: { marginBottom: 8 }, children: name }),
1707
+ /* @__PURE__ */ jsx20("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
1708
+ /* @__PURE__ */ jsx20(OAuthErrorBanner, {}),
1709
+ /* @__PURE__ */ jsx20(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
1710
+ /* @__PURE__ */ jsxs14(
418
1711
  "div",
419
1712
  {
420
1713
  "aria-hidden": "true",
@@ -427,19 +1720,19 @@ function DefaultLoginScreen({ onNavigate }) {
427
1720
  fontSize: 12
428
1721
  },
429
1722
  children: [
430
- /* @__PURE__ */ jsx10("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1723
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
431
1724
  "ou",
432
- /* @__PURE__ */ jsx10("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1725
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
433
1726
  ]
434
1727
  }
435
1728
  ),
436
- /* @__PURE__ */ jsxs4("form", { onSubmit: (e) => {
1729
+ /* @__PURE__ */ jsxs14("form", { onSubmit: (e) => {
437
1730
  e.preventDefault();
438
1731
  void f.submit();
439
1732
  }, children: [
440
- /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
1733
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
441
1734
  "E-mail",
442
- /* @__PURE__ */ jsx10(
1735
+ /* @__PURE__ */ jsx20(
443
1736
  "input",
444
1737
  {
445
1738
  "data-testid": "login-email",
@@ -449,11 +1742,11 @@ function DefaultLoginScreen({ onNavigate }) {
449
1742
  style: { display: "block", width: "100%" }
450
1743
  }
451
1744
  ),
452
- f.emailError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.emailError })
1745
+ f.emailError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.emailError })
453
1746
  ] }),
454
- /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
1747
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
455
1748
  "Senha",
456
- /* @__PURE__ */ jsx10(
1749
+ /* @__PURE__ */ jsx20(
457
1750
  "input",
458
1751
  {
459
1752
  "data-testid": "login-password",
@@ -463,10 +1756,10 @@ function DefaultLoginScreen({ onNavigate }) {
463
1756
  style: { display: "block", width: "100%" }
464
1757
  }
465
1758
  ),
466
- f.passwordError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.passwordError })
1759
+ f.passwordError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.passwordError })
467
1760
  ] }),
468
- f.error && /* @__PURE__ */ jsx10("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
469
- /* @__PURE__ */ jsx10(
1761
+ f.error && /* @__PURE__ */ jsx20("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1762
+ /* @__PURE__ */ jsx20(
470
1763
  "button",
471
1764
  {
472
1765
  "data-testid": "login-submit",
@@ -485,25 +1778,25 @@ function DefaultLoginScreen({ onNavigate }) {
485
1778
  }
486
1779
  )
487
1780
  ] }),
488
- /* @__PURE__ */ jsxs4("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
489
- /* @__PURE__ */ jsx10("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
490
- /* @__PURE__ */ jsx10("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
1781
+ /* @__PURE__ */ jsxs14("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
1782
+ /* @__PURE__ */ jsx20("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
1783
+ /* @__PURE__ */ jsx20("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
491
1784
  ] })
492
1785
  ] });
493
1786
  }
494
1787
 
495
1788
  // src/hooks/useSignupForm.ts
496
- import { useCallback as useCallback3, useMemo as useMemo3, useState as useState5 } from "react";
1789
+ import { useCallback as useCallback4, useMemo as useMemo3, useState as useState8 } from "react";
497
1790
  import { useHook as useHook5 } from "@hook-sdk/sdk";
498
1791
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
499
1792
  var MIN_PASSWORD2 = 8;
500
1793
  function useSignupForm() {
501
1794
  const { auth } = useHook5();
502
- const [name, setName] = useState5("");
503
- const [email, setEmail] = useState5("");
504
- const [password, setPassword] = useState5("");
505
- const [submitting, setSubmitting] = useState5(false);
506
- const [error, setError] = useState5(null);
1795
+ const [name, setName] = useState8("");
1796
+ const [email, setEmail] = useState8("");
1797
+ const [password, setPassword] = useState8("");
1798
+ const [submitting, setSubmitting] = useState8(false);
1799
+ const [error, setError] = useState8(null);
507
1800
  const nameError = useMemo3(() => {
508
1801
  if (name.length === 0) return null;
509
1802
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -520,7 +1813,7 @@ function useSignupForm() {
520
1813
  return null;
521
1814
  }, [password]);
522
1815
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
523
- const submit = useCallback3(async () => {
1816
+ const submit = useCallback4(async () => {
524
1817
  if (!canSubmit) return false;
525
1818
  setSubmitting(true);
526
1819
  setError(null);
@@ -553,16 +1846,16 @@ function useSignupForm() {
553
1846
  }
554
1847
 
555
1848
  // src/defaults/DefaultSignupScreen.tsx
556
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1849
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
557
1850
  function DefaultSignupScreen({ onNavigate }) {
558
1851
  const { name } = useTemplateConfig();
559
1852
  const f = useSignupForm();
560
- return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
561
- /* @__PURE__ */ jsx11("h1", { style: { marginBottom: 8 }, children: name }),
562
- /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
563
- /* @__PURE__ */ jsx11(OAuthErrorBanner, {}),
564
- /* @__PURE__ */ jsx11(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
565
- /* @__PURE__ */ jsxs5(
1853
+ return /* @__PURE__ */ jsxs15("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1854
+ /* @__PURE__ */ jsx21("h1", { style: { marginBottom: 8 }, children: name }),
1855
+ /* @__PURE__ */ jsx21("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
1856
+ /* @__PURE__ */ jsx21(OAuthErrorBanner, {}),
1857
+ /* @__PURE__ */ jsx21(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
1858
+ /* @__PURE__ */ jsxs15(
566
1859
  "div",
567
1860
  {
568
1861
  "aria-hidden": "true",
@@ -575,55 +1868,55 @@ function DefaultSignupScreen({ onNavigate }) {
575
1868
  fontSize: 12
576
1869
  },
577
1870
  children: [
578
- /* @__PURE__ */ jsx11("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1871
+ /* @__PURE__ */ jsx21("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
579
1872
  "ou",
580
- /* @__PURE__ */ jsx11("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1873
+ /* @__PURE__ */ jsx21("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
581
1874
  ]
582
1875
  }
583
1876
  ),
584
- /* @__PURE__ */ jsxs5("form", { onSubmit: (e) => {
1877
+ /* @__PURE__ */ jsxs15("form", { onSubmit: (e) => {
585
1878
  e.preventDefault();
586
1879
  void f.submit();
587
1880
  }, children: [
588
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1881
+ /* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
589
1882
  "Nome",
590
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
591
- f.nameError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.nameError })
1883
+ /* @__PURE__ */ jsx21("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
1884
+ f.nameError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.nameError })
592
1885
  ] }),
593
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1886
+ /* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
594
1887
  "E-mail",
595
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
596
- f.emailError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.emailError })
1888
+ /* @__PURE__ */ jsx21("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1889
+ f.emailError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.emailError })
597
1890
  ] }),
598
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1891
+ /* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
599
1892
  "Senha",
600
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
601
- f.passwordError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.passwordError })
1893
+ /* @__PURE__ */ jsx21("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
1894
+ f.passwordError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.passwordError })
602
1895
  ] }),
603
- f.error && /* @__PURE__ */ jsx11("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
604
- /* @__PURE__ */ jsx11("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" })
1896
+ f.error && /* @__PURE__ */ jsx21("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1897
+ /* @__PURE__ */ jsx21("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" })
605
1898
  ] }),
606
- /* @__PURE__ */ jsx11("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx11("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
1899
+ /* @__PURE__ */ jsx21("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx21("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
607
1900
  ] });
608
1901
  }
609
1902
 
610
1903
  // src/hooks/useForgotForm.ts
611
- import { useCallback as useCallback4, useMemo as useMemo4, useState as useState6 } from "react";
1904
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState9 } from "react";
612
1905
  import { useHook as useHook6 } from "@hook-sdk/sdk";
613
1906
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
614
1907
  function useForgotForm() {
615
1908
  const { auth } = useHook6();
616
- const [email, setEmail] = useState6("");
617
- const [submitting, setSubmitting] = useState6(false);
618
- const [sent, setSent] = useState6(false);
619
- const [error, setError] = useState6(null);
1909
+ const [email, setEmail] = useState9("");
1910
+ const [submitting, setSubmitting] = useState9(false);
1911
+ const [sent, setSent] = useState9(false);
1912
+ const [error, setError] = useState9(null);
620
1913
  const emailError = useMemo4(() => {
621
1914
  if (email.length === 0) return null;
622
1915
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
623
1916
  return null;
624
1917
  }, [email]);
625
1918
  const canSubmit = email.length > 0 && emailError === null && !submitting;
626
- const submit = useCallback4(async () => {
1919
+ const submit = useCallback5(async () => {
627
1920
  if (!canSubmit) return false;
628
1921
  setSubmitting(true);
629
1922
  setError(null);
@@ -651,49 +1944,49 @@ function useForgotForm() {
651
1944
  }
652
1945
 
653
1946
  // src/defaults/DefaultForgotScreen.tsx
654
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1947
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
655
1948
  function DefaultForgotScreen({ onNavigate }) {
656
1949
  const { name } = useTemplateConfig();
657
1950
  const f = useForgotForm();
658
1951
  if (f.sent) {
659
- return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
660
- /* @__PURE__ */ jsx12("h1", { children: "Verifique seu e-mail" }),
661
- /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
662
- /* @__PURE__ */ jsx12("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
1952
+ return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
1953
+ /* @__PURE__ */ jsx22("h1", { children: "Verifique seu e-mail" }),
1954
+ /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
1955
+ /* @__PURE__ */ jsx22("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
663
1956
  ] });
664
1957
  }
665
- return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
666
- /* @__PURE__ */ jsx12("h1", { style: { marginBottom: 8 }, children: name }),
667
- /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
668
- /* @__PURE__ */ jsxs6("form", { onSubmit: (e) => {
1958
+ return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1959
+ /* @__PURE__ */ jsx22("h1", { style: { marginBottom: 8 }, children: name }),
1960
+ /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
1961
+ /* @__PURE__ */ jsxs16("form", { onSubmit: (e) => {
669
1962
  e.preventDefault();
670
1963
  void f.submit();
671
1964
  }, children: [
672
- /* @__PURE__ */ jsxs6("label", { style: { display: "block", marginBottom: 12 }, children: [
1965
+ /* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
673
1966
  "E-mail",
674
- /* @__PURE__ */ jsx12("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
675
- f.emailError && /* @__PURE__ */ jsx12("small", { style: { color: "#c00" }, children: f.emailError })
1967
+ /* @__PURE__ */ jsx22("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1968
+ f.emailError && /* @__PURE__ */ jsx22("small", { style: { color: "#c00" }, children: f.emailError })
676
1969
  ] }),
677
- f.error && /* @__PURE__ */ jsx12("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
678
- /* @__PURE__ */ jsx12("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" })
1970
+ f.error && /* @__PURE__ */ jsx22("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1971
+ /* @__PURE__ */ jsx22("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" })
679
1972
  ] }),
680
- /* @__PURE__ */ jsx12("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx12("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
1973
+ /* @__PURE__ */ jsx22("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx22("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
681
1974
  ] });
682
1975
  }
683
1976
 
684
1977
  // src/hooks/useResetForm.ts
685
- import { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo5, useState as useState7 } from "react";
1978
+ import { useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo5, useState as useState10 } from "react";
686
1979
  import { useHook as useHook7 } from "@hook-sdk/sdk";
687
1980
  var MIN_PASSWORD3 = 12;
688
1981
  function useResetForm() {
689
1982
  const { auth } = useHook7();
690
- const [token, setToken] = useState7(null);
691
- const [password, setPassword] = useState7("");
692
- const [confirm, setConfirm] = useState7("");
693
- const [submitting, setSubmitting] = useState7(false);
694
- const [done, setDone] = useState7(false);
695
- const [error, setError] = useState7(null);
696
- useEffect4(() => {
1983
+ const [token, setToken] = useState10(null);
1984
+ const [password, setPassword] = useState10("");
1985
+ const [confirm, setConfirm] = useState10("");
1986
+ const [submitting, setSubmitting] = useState10(false);
1987
+ const [done, setDone] = useState10(false);
1988
+ const [error, setError] = useState10(null);
1989
+ useEffect6(() => {
697
1990
  if (typeof window === "undefined") return;
698
1991
  const params = new URLSearchParams(window.location.search);
699
1992
  const t = params.get("token");
@@ -710,7 +2003,7 @@ function useResetForm() {
710
2003
  return null;
711
2004
  }, [confirm, password]);
712
2005
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
713
- const submit = useCallback5(async () => {
2006
+ const submit = useCallback6(async () => {
714
2007
  if (!canSubmit || token === null) return;
715
2008
  setSubmitting(true);
716
2009
  setError(null);
@@ -746,67 +2039,67 @@ function useResetForm() {
746
2039
  }
747
2040
 
748
2041
  // src/defaults/DefaultResetScreen.tsx
749
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
2042
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
750
2043
  function DefaultResetScreen({ onNavigate }) {
751
2044
  const { name } = useTemplateConfig();
752
2045
  const f = useResetForm();
753
2046
  if (f.done) {
754
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
755
- /* @__PURE__ */ jsx13("h1", { children: "Senha alterada" }),
756
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
757
- /* @__PURE__ */ jsx13("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
2047
+ return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
2048
+ /* @__PURE__ */ jsx23("h1", { children: "Senha alterada" }),
2049
+ /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
2050
+ /* @__PURE__ */ jsx23("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
758
2051
  ] });
759
2052
  }
760
2053
  if (f.token === null) {
761
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
762
- /* @__PURE__ */ jsx13("h1", { children: "Link inv\xE1lido" }),
763
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
764
- /* @__PURE__ */ jsx13("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
2054
+ return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
2055
+ /* @__PURE__ */ jsx23("h1", { children: "Link inv\xE1lido" }),
2056
+ /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
2057
+ /* @__PURE__ */ jsx23("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
765
2058
  ] });
766
2059
  }
767
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
768
- /* @__PURE__ */ jsx13("h1", { style: { marginBottom: 8 }, children: name }),
769
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
770
- /* @__PURE__ */ jsxs7("form", { onSubmit: (e) => {
2060
+ return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
2061
+ /* @__PURE__ */ jsx23("h1", { style: { marginBottom: 8 }, children: name }),
2062
+ /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
2063
+ /* @__PURE__ */ jsxs17("form", { onSubmit: (e) => {
771
2064
  e.preventDefault();
772
2065
  void f.submit();
773
2066
  }, children: [
774
- /* @__PURE__ */ jsxs7("label", { style: { display: "block", marginBottom: 12 }, children: [
2067
+ /* @__PURE__ */ jsxs17("label", { style: { display: "block", marginBottom: 12 }, children: [
775
2068
  "Nova senha",
776
- /* @__PURE__ */ jsx13("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" }),
777
- f.passwordError && /* @__PURE__ */ jsx13("small", { style: { color: "#c00" }, children: f.passwordError })
2069
+ /* @__PURE__ */ jsx23("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" }),
2070
+ f.passwordError && /* @__PURE__ */ jsx23("small", { style: { color: "#c00" }, children: f.passwordError })
778
2071
  ] }),
779
- /* @__PURE__ */ jsxs7("label", { style: { display: "block", marginBottom: 12 }, children: [
2072
+ /* @__PURE__ */ jsxs17("label", { style: { display: "block", marginBottom: 12 }, children: [
780
2073
  "Confirmar senha",
781
- /* @__PURE__ */ jsx13("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" }),
782
- f.confirmError && /* @__PURE__ */ jsx13("small", { style: { color: "#c00" }, children: f.confirmError })
2074
+ /* @__PURE__ */ jsx23("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" }),
2075
+ f.confirmError && /* @__PURE__ */ jsx23("small", { style: { color: "#c00" }, children: f.confirmError })
783
2076
  ] }),
784
- f.error && /* @__PURE__ */ jsx13("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
785
- /* @__PURE__ */ jsx13("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" })
2077
+ f.error && /* @__PURE__ */ jsx23("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
2078
+ /* @__PURE__ */ jsx23("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" })
786
2079
  ] })
787
2080
  ] });
788
2081
  }
789
2082
 
790
2083
  // src/defaults/DefaultPaywall.tsx
791
- import { useState as useState8 } from "react";
792
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
2084
+ import { useState as useState11 } from "react";
2085
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
793
2086
  function DefaultPaywall() {
794
2087
  const config = useTemplateConfig();
795
2088
  const { checkout, opening, error } = usePaywallState();
796
2089
  const p = config.subscription.paywall_config;
797
- const [cpf, setCpf] = useState8("");
2090
+ const [cpf, setCpf] = useState11("");
798
2091
  const cpfDigits = cpf.replace(/\D/g, "");
799
2092
  const canCheckout = cpfDigits.length === 11 && !opening;
800
- return /* @__PURE__ */ jsxs8("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
801
- /* @__PURE__ */ jsx14("h1", { style: { marginBottom: 8 }, children: p.title }),
802
- p.subtitle && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
803
- /* @__PURE__ */ jsx14("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs8("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
804
- /* @__PURE__ */ jsx14("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
805
- /* @__PURE__ */ jsx14("span", { children: b })
2093
+ return /* @__PURE__ */ jsxs18("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
2094
+ /* @__PURE__ */ jsx24("h1", { style: { marginBottom: 8 }, children: p.title }),
2095
+ p.subtitle && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
2096
+ /* @__PURE__ */ jsx24("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs18("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
2097
+ /* @__PURE__ */ jsx24("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
2098
+ /* @__PURE__ */ jsx24("span", { children: b })
806
2099
  ] }, b)) }),
807
- /* @__PURE__ */ jsxs8("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
808
- /* @__PURE__ */ jsx14("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
809
- /* @__PURE__ */ jsx14(
2100
+ /* @__PURE__ */ jsxs18("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
2101
+ /* @__PURE__ */ jsx24("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
2102
+ /* @__PURE__ */ jsx24(
810
2103
  "input",
811
2104
  {
812
2105
  "data-testid": "paywall-cpf",
@@ -819,8 +2112,8 @@ function DefaultPaywall() {
819
2112
  }
820
2113
  )
821
2114
  ] }),
822
- error && /* @__PURE__ */ jsx14("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
823
- /* @__PURE__ */ jsx14(
2115
+ error && /* @__PURE__ */ jsx24("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
2116
+ /* @__PURE__ */ jsx24(
824
2117
  "button",
825
2118
  {
826
2119
  "data-testid": "paywall-cta",
@@ -841,21 +2134,21 @@ function DefaultPaywall() {
841
2134
  children: opening ? "Abrindo..." : p.cta
842
2135
  }
843
2136
  ),
844
- p.priceHint && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
845
- p.footerNote && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
2137
+ p.priceHint && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
2138
+ p.footerNote && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
846
2139
  ] });
847
2140
  }
848
2141
 
849
2142
  // src/AppRoot.tsx
850
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
2143
+ import { Fragment as Fragment6, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
851
2144
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
852
2145
  function PaymentReturnHandler({ children }) {
853
2146
  const { subscription } = useHook8();
854
- const subRef = useRef(subscription);
2147
+ const subRef = useRef2(subscription);
855
2148
  subRef.current = subscription;
856
- const runIdRef = useRef(0);
857
- const [state, setState] = useState9("idle");
858
- const runPoll = useCallback6(() => {
2149
+ const runIdRef = useRef2(0);
2150
+ const [state, setState] = useState12("idle");
2151
+ const runPoll = useCallback7(() => {
859
2152
  const runId = ++runIdRef.current;
860
2153
  setState("confirming");
861
2154
  let attempts = 0;
@@ -884,7 +2177,7 @@ function PaymentReturnHandler({ children }) {
884
2177
  };
885
2178
  void tick();
886
2179
  }, []);
887
- useEffect5(() => {
2180
+ useEffect7(() => {
888
2181
  if (typeof window === "undefined") return;
889
2182
  const url = new URL(window.location.href);
890
2183
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -894,20 +2187,20 @@ function PaymentReturnHandler({ children }) {
894
2187
  };
895
2188
  }, [runPoll]);
896
2189
  if (state === "confirming") {
897
- return /* @__PURE__ */ jsx15(
2190
+ return /* @__PURE__ */ jsx25(
898
2191
  "div",
899
2192
  {
900
2193
  role: "status",
901
2194
  "aria-live": "polite",
902
- style: overlayStyle,
2195
+ style: overlayStyle2,
903
2196
  children: "Confirmando pagamento\u2026"
904
2197
  }
905
2198
  );
906
2199
  }
907
2200
  if (state === "waiting") {
908
- return /* @__PURE__ */ jsx15("div", { role: "status", "aria-live": "polite", style: overlayStyle, children: /* @__PURE__ */ jsxs9("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
909
- /* @__PURE__ */ jsx15("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
910
- /* @__PURE__ */ jsx15(
2201
+ return /* @__PURE__ */ jsx25("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs19("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2202
+ /* @__PURE__ */ jsx25("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2203
+ /* @__PURE__ */ jsx25(
911
2204
  "button",
912
2205
  {
913
2206
  type: "button",
@@ -918,9 +2211,9 @@ function PaymentReturnHandler({ children }) {
918
2211
  )
919
2212
  ] }) });
920
2213
  }
921
- return /* @__PURE__ */ jsx15(Fragment5, { children });
2214
+ return /* @__PURE__ */ jsx25(Fragment6, { children });
922
2215
  }
923
- var overlayStyle = {
2216
+ var overlayStyle2 = {
924
2217
  position: "fixed",
925
2218
  inset: 0,
926
2219
  display: "flex",
@@ -951,14 +2244,14 @@ function AppRoot({
951
2244
  Reset = DefaultResetScreen,
952
2245
  Paywall = DefaultPaywall
953
2246
  }) {
954
- return /* @__PURE__ */ jsx15(PaymentReturnHandler, { children: /* @__PURE__ */ jsx15(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx15(ErrorBoundary, { children: /* @__PURE__ */ jsx15(ThemeProvider, { children: /* @__PURE__ */ jsx15(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx15(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs9(SubscriptionGate, { Paywall, children: [
2247
+ return /* @__PURE__ */ jsx25(PaymentReturnHandler, { children: /* @__PURE__ */ jsx25(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx25(ErrorBoundary, { children: /* @__PURE__ */ jsx25(ThemeProvider, { children: /* @__PURE__ */ jsx25(InstallGate, { children: /* @__PURE__ */ jsx25(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx25(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs19(SubscriptionGate, { Paywall, children: [
955
2248
  children,
956
- /* @__PURE__ */ jsx15(PushPrompt, {})
957
- ] }) }) }) }) }) }) });
2249
+ /* @__PURE__ */ jsx25(PushPrompt, {})
2250
+ ] }) }) }) }) }) }) }) });
958
2251
  }
959
2252
 
960
2253
  // src/hooks/usePush.ts
961
- import { useCallback as useCallback7, useEffect as useEffect6, useState as useState10 } from "react";
2254
+ import { useCallback as useCallback8, useEffect as useEffect8, useState as useState13 } from "react";
962
2255
  import { useHook as useHook9 } from "@hook-sdk/sdk";
963
2256
  function detectIosNeedsInstall() {
964
2257
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
@@ -986,11 +2279,11 @@ function deriveState(push) {
986
2279
  }
987
2280
  function usePush() {
988
2281
  const { push } = useHook9();
989
- const [state, setState] = useState10(() => deriveState(push));
990
- useEffect6(() => {
2282
+ const [state, setState] = useState13(() => deriveState(push));
2283
+ useEffect8(() => {
991
2284
  setState(deriveState(push));
992
2285
  }, [push]);
993
- const subscribe = useCallback7(async () => {
2286
+ const subscribe = useCallback8(async () => {
994
2287
  try {
995
2288
  await push.subscribe();
996
2289
  setState({ kind: "subscribed" });
@@ -1002,7 +2295,7 @@ function usePush() {
1002
2295
  throw e;
1003
2296
  }
1004
2297
  }, [push]);
1005
- const unsubscribe = useCallback7(async () => {
2298
+ const unsubscribe = useCallback8(async () => {
1006
2299
  try {
1007
2300
  await push.unsubscribe();
1008
2301
  setState({ kind: "prompt" });
@@ -1015,31 +2308,31 @@ function usePush() {
1015
2308
  }
1016
2309
 
1017
2310
  // src/components/PushPrompt.tsx
1018
- import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
2311
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1019
2312
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
1020
2313
  const { state, subscribe } = usePush();
1021
2314
  if (state.kind === "subscribed") return null;
1022
2315
  if (state.kind === "ios_needs_install") {
1023
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
1024
- /* @__PURE__ */ jsx16("h3", { children: texts.iosInstallTitle }),
1025
- /* @__PURE__ */ jsx16("p", { children: texts.iosInstallBody }),
1026
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx16("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2316
+ return /* @__PURE__ */ jsxs20("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2317
+ /* @__PURE__ */ jsx26("h3", { children: texts.iosInstallTitle }),
2318
+ /* @__PURE__ */ jsx26("p", { children: texts.iosInstallBody }),
2319
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx26("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
1027
2320
  ] });
1028
2321
  }
1029
2322
  if (state.kind === "denied") {
1030
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
1031
- /* @__PURE__ */ jsx16("h3", { children: texts.deniedTitle }),
1032
- /* @__PURE__ */ jsx16("p", { children: texts.deniedBody })
2323
+ return /* @__PURE__ */ jsxs20("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2324
+ /* @__PURE__ */ jsx26("h3", { children: texts.deniedTitle }),
2325
+ /* @__PURE__ */ jsx26("p", { children: texts.deniedBody })
1033
2326
  ] });
1034
2327
  }
1035
2328
  if (state.kind === "unsupported") {
1036
- return /* @__PURE__ */ jsx16("div", { className, role: "region", children: /* @__PURE__ */ jsx16("p", { children: texts.unsupportedBody }) });
2329
+ return /* @__PURE__ */ jsx26("div", { className, role: "region", children: /* @__PURE__ */ jsx26("p", { children: texts.unsupportedBody }) });
1037
2330
  }
1038
2331
  if (state.kind === "error") {
1039
- return /* @__PURE__ */ jsx16("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx16("p", { children: state.message }) });
2332
+ return /* @__PURE__ */ jsx26("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx26("p", { children: state.message }) });
1040
2333
  }
1041
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", children: [
1042
- /* @__PURE__ */ jsx16(
2334
+ return /* @__PURE__ */ jsxs20("div", { className, role: "region", children: [
2335
+ /* @__PURE__ */ jsx26(
1043
2336
  "button",
1044
2337
  {
1045
2338
  type: "button",
@@ -1053,27 +2346,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
1053
2346
  children: texts.cta
1054
2347
  }
1055
2348
  ),
1056
- onDeclined && /* @__PURE__ */ jsx16("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2349
+ onDeclined && /* @__PURE__ */ jsx26("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
1057
2350
  ] });
1058
2351
  }
1059
2352
 
1060
2353
  // src/defaults/EmptyState.tsx
1061
- import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
2354
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
1062
2355
  function EmptyState({ title, description, action }) {
1063
- return /* @__PURE__ */ jsxs11("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
1064
- /* @__PURE__ */ jsx17("h2", { style: { marginBottom: 8 }, children: title }),
1065
- description && /* @__PURE__ */ jsx17("p", { style: { opacity: 0.7 }, children: description }),
1066
- action && /* @__PURE__ */ jsx17("div", { style: { marginTop: 16 }, children: action })
2356
+ return /* @__PURE__ */ jsxs21("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2357
+ /* @__PURE__ */ jsx27("h2", { style: { marginBottom: 8 }, children: title }),
2358
+ description && /* @__PURE__ */ jsx27("p", { style: { opacity: 0.7 }, children: description }),
2359
+ action && /* @__PURE__ */ jsx27("div", { style: { marginTop: 16 }, children: action })
1067
2360
  ] });
1068
2361
  }
1069
2362
 
1070
2363
  // src/hooks/useAuthPrimitives.ts
1071
- import { useEffect as useEffect7 } from "react";
2364
+ import { useEffect as useEffect9 } from "react";
1072
2365
  import { useHook as useHook10 } from "@hook-sdk/sdk";
1073
2366
  var warned = false;
1074
2367
  function useAuthPrimitives() {
1075
2368
  const { auth } = useHook10();
1076
- useEffect7(() => {
2369
+ useEffect9(() => {
1077
2370
  if (!warned && process.env.NODE_ENV !== "production") {
1078
2371
  warned = true;
1079
2372
  console.warn(
@@ -1104,14 +2397,14 @@ function useSubscription() {
1104
2397
  }
1105
2398
 
1106
2399
  // src/hooks/useReminders.ts
1107
- import { useCallback as useCallback8, useEffect as useEffect8, useState as useState11 } from "react";
2400
+ import { useCallback as useCallback9, useEffect as useEffect10, useState as useState14 } from "react";
1108
2401
  import { useHook as useHook12 } from "@hook-sdk/sdk";
1109
2402
  function useReminders() {
1110
2403
  const { push } = useHook12();
1111
2404
  const r = push.reminders;
1112
- const [reminders, setReminders] = useState11([]);
1113
- const [loading, setLoading] = useState11(true);
1114
- const reload = useCallback8(async () => {
2405
+ const [reminders, setReminders] = useState14([]);
2406
+ const [loading, setLoading] = useState14(true);
2407
+ const reload = useCallback9(async () => {
1115
2408
  setLoading(true);
1116
2409
  try {
1117
2410
  const next = await r.list();
@@ -1120,38 +2413,38 @@ function useReminders() {
1120
2413
  setLoading(false);
1121
2414
  }
1122
2415
  }, [r]);
1123
- useEffect8(() => {
2416
+ useEffect10(() => {
1124
2417
  void reload();
1125
2418
  }, [reload]);
1126
- const setReminder = useCallback8(async (input) => {
2419
+ const setReminder = useCallback9(async (input) => {
1127
2420
  await r.set(input);
1128
2421
  await reload();
1129
2422
  }, [r, reload]);
1130
- const deleteReminder = useCallback8(async (slot) => {
2423
+ const deleteReminder = useCallback9(async (slot) => {
1131
2424
  await r.delete(slot);
1132
2425
  await reload();
1133
2426
  }, [r, reload]);
1134
- const schedule = useCallback8(async (items) => {
2427
+ const schedule = useCallback9(async (items) => {
1135
2428
  return r.schedule(items);
1136
2429
  }, [r]);
1137
- const setFallbacks = useCallback8(async (items) => {
2430
+ const setFallbacks = useCallback9(async (items) => {
1138
2431
  return r.setFallbacks(items);
1139
2432
  }, [r]);
1140
2433
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
1141
2434
  }
1142
2435
 
1143
2436
  // src/hooks/useToast.ts
1144
- import { useCallback as useCallback9, useState as useState12 } from "react";
2437
+ import { useCallback as useCallback10, useState as useState15 } from "react";
1145
2438
  function useToast() {
1146
- const [items, setItems] = useState12([]);
1147
- const show = useCallback9((message, kind = "info") => {
2439
+ const [items, setItems] = useState15([]);
2440
+ const show = useCallback10((message, kind = "info") => {
1148
2441
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1149
2442
  setItems((prev) => [...prev, { id, message, kind }]);
1150
2443
  setTimeout(() => {
1151
2444
  setItems((prev) => prev.filter((t) => t.id !== id));
1152
2445
  }, 4e3);
1153
2446
  }, []);
1154
- const dismiss = useCallback9((id) => {
2447
+ const dismiss = useCallback10((id) => {
1155
2448
  setItems((prev) => prev.filter((t) => t.id !== id));
1156
2449
  }, []);
1157
2450
  return { items, show, dismiss };
@@ -1165,11 +2458,21 @@ export {
1165
2458
  DefaultSignupScreen,
1166
2459
  EmptyState,
1167
2460
  ErrorBoundary,
2461
+ InstallGate,
2462
+ InstallSplash,
1168
2463
  LoadingState,
1169
2464
  PushPrompt2 as PushPrompt,
2465
+ detectAndroidBrowser,
2466
+ detectIOSBrowser,
2467
+ detectInAppApp,
2468
+ detectPlatform,
2469
+ detectStandalone,
2470
+ shouldBlockInstall,
2471
+ shouldShowPermanentOption,
1170
2472
  useAuth,
1171
2473
  useAuthPrimitives,
1172
2474
  useForgotForm,
2475
+ useInstallPrompt,
1173
2476
  useLoginForm,
1174
2477
  usePaywallState,
1175
2478
  usePush,