@hook-sdk/template 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/AppRoot.tsx
2
- import { useCallback as useCallback6, useEffect as useEffect4, useRef, useState as useState8 } 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,1225 @@ 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 redirectToSafari = useCallback2(() => {
406
+ if (typeof location === "undefined") return;
407
+ track("pwa_install_redirect_to_safari", { slug });
408
+ const url = `safari-https://${location.host}${location.pathname}${location.search}${location.hash}`;
409
+ try {
410
+ window.location.href = url;
411
+ } catch {
412
+ }
413
+ }, [slug]);
414
+ const copyLink = useCallback2(async () => {
415
+ if (typeof navigator === "undefined" || typeof location === "undefined") return;
416
+ try {
417
+ await navigator.clipboard?.writeText?.(location.href);
418
+ } catch {
419
+ }
420
+ }, []);
421
+ const reset = useCallback2(() => {
422
+ const storage = safeStorage();
423
+ if (storage) {
424
+ storage.removeItem(storageKey.dismissedAt(slug));
425
+ storage.removeItem(storageKey.installedAt(slug));
426
+ storage.removeItem(storageKey.skipCount(slug));
427
+ }
428
+ if (typeof sessionStorage !== "undefined") {
429
+ try {
430
+ sessionStorage.removeItem(storageKey.sessionSkip(slug));
431
+ } catch {
432
+ }
433
+ }
434
+ setIsDismissedSession(false);
435
+ setIsDismissedPermanent(false);
436
+ setSkipCount(0);
437
+ }, [slug]);
438
+ return {
439
+ platform,
440
+ iosBrowser,
441
+ androidBrowser,
442
+ inAppApp,
443
+ isInstallable,
444
+ isInstalled,
445
+ isDismissedSession,
446
+ isDismissedPermanent,
447
+ skipCount,
448
+ variant,
449
+ promptInstall,
450
+ dismissSession,
451
+ dismissPermanent,
452
+ redirectToSafari,
453
+ copyLink,
454
+ reset
455
+ };
456
+ }
457
+ function shouldBlockInstall(state, now = Date.now()) {
458
+ if (state.isInstalled) return false;
459
+ if (state.variant === "none") return false;
460
+ if (state.isDismissedSession) return false;
461
+ if (state.isDismissedPermanent) {
462
+ void now;
463
+ return false;
464
+ }
465
+ if (state.platform === "desktop" && !state.isInstallable) return false;
466
+ if (state.platform === "unknown") return false;
467
+ return true;
468
+ }
469
+ function shouldShowPermanentOption(state) {
470
+ return state.skipCount >= SESSION_SKIPS_BEFORE_PERMANENT_OPTION;
471
+ }
472
+
473
+ // src/components/InstallGate/copy.ts
474
+ var INSTALL_COPY = {
475
+ android: {
476
+ native: {
477
+ title: "Instale no seu celular",
478
+ subtitle: "Acesso r\xE1pido, sem precisar do navegador",
479
+ cta: "Baixar",
480
+ skip: "Continuar no navegador",
481
+ skipPermanent: "N\xE3o me pergunte mais"
482
+ },
483
+ manual: {
484
+ title: "Instale em 2 toques",
485
+ subtitle: 'Toque no menu do navegador e escolha "Instalar aplicativo"',
486
+ step1: "Toque no menu do navegador",
487
+ step2: 'Escolha "Instalar aplicativo"',
488
+ cta: "Entendi",
489
+ skip: "Continuar no navegador",
490
+ skipPermanent: "N\xE3o me pergunte mais"
491
+ }
492
+ },
493
+ iosSafari: {
494
+ title: "Adicione \xE0 sua Tela de In\xEDcio",
495
+ subtitle: "Siga os 3 passos",
496
+ step1: {
497
+ title: "Toque em Compartilhar",
498
+ subtitle: "Na barra inferior do Safari"
499
+ },
500
+ step2: {
501
+ title: 'Role e toque em "Adicionar \xE0 Tela de In\xEDcio"',
502
+ iconLabel: "Adicionar \xE0 Tela de In\xEDcio"
503
+ },
504
+ step3: {
505
+ title: 'Toque em "Adicionar" pra confirmar',
506
+ buttonLabel: "Adicionar"
507
+ },
508
+ skip: "Continuar no Safari",
509
+ skipPermanent: "N\xE3o me pergunte mais"
510
+ },
511
+ iosOther: {
512
+ title: "Abra no Safari pra instalar",
513
+ subtitle: "No Chrome/Firefox/Edge do iPhone n\xE3o d\xE1 pra instalar PWA. Abra o link no Safari.",
514
+ ctaPrimary: "Abrir no Safari",
515
+ ctaSecondary: "Copiar link",
516
+ copiedToast: "Link copiado. Cole no Safari.",
517
+ skip: "Continuar mesmo assim",
518
+ skipPermanent: "N\xE3o me pergunte mais"
519
+ },
520
+ inApp: {
521
+ instagram: {
522
+ title: "Pra instalar, abra fora do Instagram",
523
+ step1: "Toque em \u22EF (canto superior direito)",
524
+ step2: 'Escolha "Abrir no navegador externo"'
525
+ },
526
+ facebook: {
527
+ title: "Pra instalar, abra fora do Facebook",
528
+ step1: "Toque em \u22EE",
529
+ step2: 'Escolha "Abrir no navegador"'
530
+ },
531
+ tiktok: {
532
+ title: "Pra instalar, abra fora do TikTok",
533
+ step1: "Toque em \u22EF",
534
+ step2: 'Escolha "Abrir no Safari" (iOS) ou "Abrir no Chrome" (Android)'
535
+ },
536
+ whatsapp: {
537
+ title: "Pra instalar, abra fora do WhatsApp",
538
+ step1: "Toque longo no link",
539
+ step2: 'Escolha "Abrir no navegador"'
540
+ },
541
+ twitter: {
542
+ title: "Pra instalar, abra fora do Twitter",
543
+ step1: "Toque em \u22EF",
544
+ step2: 'Escolha "Abrir no navegador"'
545
+ },
546
+ linkedin: {
547
+ title: "Pra instalar, abra fora do LinkedIn",
548
+ step1: "Toque em \u22EF",
549
+ step2: 'Escolha "Abrir no navegador"'
550
+ },
551
+ telegram: {
552
+ title: "Pra instalar, abra fora do Telegram",
553
+ step1: "Toque em \u22EE",
554
+ step2: 'Escolha "Abrir no navegador"'
555
+ },
556
+ line: {
557
+ title: "Pra instalar, abra fora do LINE",
558
+ step1: "Toque em \u22EF",
559
+ step2: 'Escolha "Abrir no navegador"'
560
+ },
561
+ snapchat: {
562
+ title: "Pra instalar, abra fora do Snapchat",
563
+ step1: "Mantenha pressionado o link",
564
+ step2: 'Escolha "Abrir no navegador"'
565
+ },
566
+ pinterest: {
567
+ title: "Pra instalar, abra fora do Pinterest",
568
+ step1: "Toque em \u22EF",
569
+ step2: 'Escolha "Abrir no navegador"'
570
+ },
571
+ wechat: {
572
+ title: "Pra instalar, abra fora do WeChat",
573
+ step1: "Toque em \u22EF",
574
+ step2: 'Escolha "Abrir no navegador"'
575
+ },
576
+ other: {
577
+ title: "Abra no navegador do celular",
578
+ step1: "Toque no menu do app atual",
579
+ step2: 'Escolha "Abrir no Chrome" ou "Abrir no Safari"'
580
+ },
581
+ copy: "Copiar link",
582
+ copiedToast: "Link copiado. Cole no Chrome/Safari.",
583
+ skip: "Continuar aqui mesmo",
584
+ skipPermanent: "N\xE3o me pergunte mais"
585
+ },
586
+ desktop: {
587
+ title: "Instale no computador",
588
+ subtitle: "Acesso r\xE1pido",
589
+ cta: "Baixar",
590
+ close: "Fechar"
591
+ }
592
+ };
593
+
594
+ // src/components/InstallGate/InstallSplash.tsx
595
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
596
+ function InstallSplash({ children, title, subtitle }) {
597
+ const { name, theme } = useTemplateConfig();
598
+ const iconUrl = theme.icon_url || theme.logo_url || null;
599
+ return /* @__PURE__ */ jsx7(
600
+ "div",
601
+ {
602
+ role: "dialog",
603
+ "aria-modal": "true",
604
+ "aria-labelledby": "install-splash-title",
605
+ "aria-describedby": subtitle ? "install-splash-subtitle" : void 0,
606
+ style: overlayStyle,
607
+ children: /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
608
+ /* @__PURE__ */ jsx7("div", { style: { display: "flex", justifyContent: "center", marginBottom: 16 }, children: iconUrl ? /* @__PURE__ */ jsx7(
609
+ "img",
610
+ {
611
+ src: iconUrl,
612
+ alt: `\xCDcone de ${name}`,
613
+ style: { width: 80, height: 80, borderRadius: 20, objectFit: "cover" }
614
+ }
615
+ ) : /* @__PURE__ */ jsx7(
616
+ "div",
617
+ {
618
+ style: {
619
+ width: 80,
620
+ height: 80,
621
+ borderRadius: 20,
622
+ background: "var(--hook-color-primary)",
623
+ color: "#fff",
624
+ display: "flex",
625
+ alignItems: "center",
626
+ justifyContent: "center",
627
+ fontSize: 36,
628
+ fontWeight: 700
629
+ },
630
+ children: name.charAt(0).toUpperCase()
631
+ }
632
+ ) }),
633
+ /* @__PURE__ */ jsx7("h1", { id: "install-splash-title", style: titleStyle, children: title }),
634
+ subtitle && /* @__PURE__ */ jsx7("p", { id: "install-splash-subtitle", style: subtitleStyle, children: subtitle }),
635
+ /* @__PURE__ */ jsx7("div", { style: { marginTop: 24 }, children }),
636
+ /* @__PURE__ */ jsx7("p", { style: footerStyle, children: "por Hook" })
637
+ ] })
638
+ }
639
+ );
640
+ }
641
+ var primaryButtonStyle = {
642
+ width: "100%",
643
+ padding: "14px 20px",
644
+ background: "var(--hook-color-primary)",
645
+ color: "#fff",
646
+ border: "none",
647
+ borderRadius: 999,
648
+ fontSize: 17,
649
+ fontWeight: 600,
650
+ cursor: "pointer",
651
+ marginBottom: 12
652
+ };
653
+ var secondaryButtonStyle = {
654
+ width: "100%",
655
+ padding: "12px 20px",
656
+ background: "transparent",
657
+ color: "var(--hook-color-primary)",
658
+ border: "1px solid var(--hook-color-primary)",
659
+ borderRadius: 999,
660
+ fontSize: 15,
661
+ fontWeight: 500,
662
+ cursor: "pointer",
663
+ marginBottom: 12
664
+ };
665
+ var skipLinkStyle = {
666
+ display: "block",
667
+ width: "100%",
668
+ padding: "10px",
669
+ marginTop: 8,
670
+ background: "transparent",
671
+ color: "#555",
672
+ border: "none",
673
+ fontSize: 14,
674
+ textDecoration: "underline",
675
+ cursor: "pointer",
676
+ textAlign: "center"
677
+ };
678
+ var skipPermanentLinkStyle = {
679
+ ...skipLinkStyle,
680
+ color: "#999",
681
+ fontSize: 13,
682
+ marginTop: 4
683
+ };
684
+ var overlayStyle = {
685
+ position: "fixed",
686
+ inset: 0,
687
+ background: "var(--hook-color-background, #fafafa)",
688
+ zIndex: 1e4,
689
+ display: "flex",
690
+ alignItems: "center",
691
+ justifyContent: "center",
692
+ padding: 20,
693
+ overflow: "auto"
694
+ };
695
+ var cardStyle = {
696
+ width: "100%",
697
+ maxWidth: 420,
698
+ padding: 24,
699
+ textAlign: "center"
700
+ };
701
+ var titleStyle = {
702
+ fontSize: 24,
703
+ fontWeight: 700,
704
+ lineHeight: 1.2,
705
+ margin: "0 0 8px 0",
706
+ color: "#111"
707
+ };
708
+ var subtitleStyle = {
709
+ fontSize: 15,
710
+ lineHeight: 1.4,
711
+ color: "#555",
712
+ margin: 0
713
+ };
714
+ var footerStyle = {
715
+ fontSize: 11,
716
+ color: "#aaa",
717
+ marginTop: 32,
718
+ letterSpacing: 0.5
719
+ };
720
+
721
+ // src/components/InstallGate/icons.tsx
722
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
723
+ var defaultSvgProps = (size) => ({
724
+ width: size,
725
+ height: size,
726
+ viewBox: "0 0 24 24",
727
+ fill: "none",
728
+ stroke: "currentColor",
729
+ strokeWidth: 2,
730
+ strokeLinecap: "round",
731
+ strokeLinejoin: "round"
732
+ });
733
+ function ShareIconIOS({ size = 24, style, className }) {
734
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
735
+ /* @__PURE__ */ jsx8("path", { d: "M12 2L12 15" }),
736
+ /* @__PURE__ */ jsx8("path", { d: "M8 6L12 2L16 6" }),
737
+ /* @__PURE__ */ jsx8("path", { d: "M4 11v9a2 2 0 002 2h12a2 2 0 002-2v-9" })
738
+ ] });
739
+ }
740
+ function MenuDotsVerticalIcon({ size = 24, style, className }) {
741
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
742
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "5", r: "1.5" }),
743
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "1.5" }),
744
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "19", r: "1.5" })
745
+ ] });
746
+ }
747
+ function MenuDotsHorizontalIcon({ size = 24, style, className }) {
748
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
749
+ /* @__PURE__ */ jsx8("circle", { cx: "5", cy: "12", r: "1.5" }),
750
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "1.5" }),
751
+ /* @__PURE__ */ jsx8("circle", { cx: "19", cy: "12", r: "1.5" })
752
+ ] });
753
+ }
754
+ function SquarePlusIcon({ size = 24, style, className }) {
755
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
756
+ /* @__PURE__ */ jsx8("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
757
+ /* @__PURE__ */ jsx8("path", { d: "M12 8v8" }),
758
+ /* @__PURE__ */ jsx8("path", { d: "M8 12h8" })
759
+ ] });
760
+ }
761
+ function DownloadIcon({ size = 24, style, className }) {
762
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
763
+ /* @__PURE__ */ jsx8("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }),
764
+ /* @__PURE__ */ jsx8("polyline", { points: "7 10 12 15 17 10" }),
765
+ /* @__PURE__ */ jsx8("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
766
+ ] });
767
+ }
768
+ function ExternalLinkIcon({ size = 24, style, className }) {
769
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
770
+ /* @__PURE__ */ jsx8("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
771
+ /* @__PURE__ */ jsx8("polyline", { points: "15 3 21 3 21 9" }),
772
+ /* @__PURE__ */ jsx8("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
773
+ ] });
774
+ }
775
+ function XIcon({ size = 20, style, className }) {
776
+ return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
777
+ /* @__PURE__ */ jsx8("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
778
+ /* @__PURE__ */ jsx8("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
779
+ ] });
780
+ }
781
+
782
+ // src/components/InstallGate/variants/AndroidNativeVariant.tsx
783
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
784
+ function AndroidNativeVariant({
785
+ state,
786
+ actions
787
+ }) {
788
+ const copy = INSTALL_COPY.android.native;
789
+ const showPermanent = shouldShowPermanentOption(state);
790
+ return /* @__PURE__ */ jsxs3(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
791
+ /* @__PURE__ */ jsxs3(
792
+ "button",
793
+ {
794
+ "data-testid": "install-prompt-cta-android-native",
795
+ type: "button",
796
+ onClick: () => void actions.promptInstall(),
797
+ style: { ...primaryButtonStyle, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8 },
798
+ children: [
799
+ /* @__PURE__ */ jsx9(DownloadIcon, { size: 18 }),
800
+ copy.cta
801
+ ]
802
+ }
803
+ ),
804
+ /* @__PURE__ */ jsx9(
805
+ "button",
806
+ {
807
+ "data-testid": "install-prompt-skip-session",
808
+ type: "button",
809
+ onClick: actions.dismissSession,
810
+ style: skipLinkStyle,
811
+ children: copy.skip
812
+ }
813
+ ),
814
+ showPermanent && /* @__PURE__ */ jsx9(
815
+ "button",
816
+ {
817
+ "data-testid": "install-prompt-skip-permanent",
818
+ type: "button",
819
+ onClick: actions.dismissPermanent,
820
+ style: skipPermanentLinkStyle,
821
+ children: copy.skipPermanent
822
+ }
823
+ )
824
+ ] });
825
+ }
826
+
827
+ // src/components/InstallGate/variants/AndroidManualVariant.tsx
828
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
829
+ function AndroidManualVariant({
830
+ state,
831
+ actions
832
+ }) {
833
+ const copy = INSTALL_COPY.android.manual;
834
+ const showPermanent = shouldShowPermanentOption(state);
835
+ return /* @__PURE__ */ jsxs4(InstallSplash, { title: copy.title, children: [
836
+ /* @__PURE__ */ jsx10(Step, { n: 1, icon: /* @__PURE__ */ jsx10(MenuDotsVerticalIcon, { size: 20 }), children: copy.step1 }),
837
+ /* @__PURE__ */ jsx10(Step, { n: 2, icon: /* @__PURE__ */ jsx10(DownloadIcon, { size: 18 }), children: copy.step2 }),
838
+ /* @__PURE__ */ jsx10(
839
+ "button",
840
+ {
841
+ "data-testid": "install-prompt-cta-android-manual",
842
+ type: "button",
843
+ onClick: actions.dismissSession,
844
+ style: primaryButtonStyle,
845
+ children: copy.cta
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx10(
849
+ "button",
850
+ {
851
+ "data-testid": "install-prompt-skip-session",
852
+ type: "button",
853
+ onClick: actions.dismissSession,
854
+ style: skipLinkStyle,
855
+ children: copy.skip
856
+ }
857
+ ),
858
+ showPermanent && /* @__PURE__ */ jsx10(
859
+ "button",
860
+ {
861
+ "data-testid": "install-prompt-skip-permanent",
862
+ type: "button",
863
+ onClick: actions.dismissPermanent,
864
+ style: skipPermanentLinkStyle,
865
+ children: copy.skipPermanent
866
+ }
867
+ )
868
+ ] });
869
+ }
870
+ function Step({ n, icon, children }) {
871
+ return /* @__PURE__ */ jsxs4(
872
+ "div",
873
+ {
874
+ style: {
875
+ display: "flex",
876
+ alignItems: "center",
877
+ gap: 12,
878
+ padding: "12px 14px",
879
+ background: "#f5f5f7",
880
+ borderRadius: 12,
881
+ marginBottom: 10,
882
+ textAlign: "left"
883
+ },
884
+ children: [
885
+ /* @__PURE__ */ jsx10(
886
+ "div",
887
+ {
888
+ style: {
889
+ width: 28,
890
+ height: 28,
891
+ borderRadius: "50%",
892
+ background: "var(--hook-color-primary)",
893
+ color: "#fff",
894
+ display: "flex",
895
+ alignItems: "center",
896
+ justifyContent: "center",
897
+ fontSize: 14,
898
+ fontWeight: 700,
899
+ flexShrink: 0
900
+ },
901
+ children: n
902
+ }
903
+ ),
904
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, fontSize: 15, color: "#333" }, children }),
905
+ /* @__PURE__ */ jsx10("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
906
+ ]
907
+ }
908
+ );
909
+ }
910
+
911
+ // src/components/InstallGate/variants/IOSafariVariant.tsx
912
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
913
+ function IOSafariVariant({
914
+ state,
915
+ actions
916
+ }) {
917
+ const copy = INSTALL_COPY.iosSafari;
918
+ const showPermanent = shouldShowPermanentOption(state);
919
+ return /* @__PURE__ */ jsxs5(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
920
+ /* @__PURE__ */ jsx11(
921
+ Step2,
922
+ {
923
+ n: 1,
924
+ title: copy.step1.title,
925
+ subtitle: copy.step1.subtitle,
926
+ visual: /* @__PURE__ */ jsx11(
927
+ "div",
928
+ {
929
+ style: {
930
+ display: "flex",
931
+ justifyContent: "center",
932
+ alignItems: "center",
933
+ background: "#f5f5f7",
934
+ borderRadius: 12,
935
+ padding: "12px 0",
936
+ marginTop: 8
937
+ },
938
+ children: /* @__PURE__ */ jsx11(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
939
+ }
940
+ )
941
+ }
942
+ ),
943
+ /* @__PURE__ */ jsx11(
944
+ Step2,
945
+ {
946
+ n: 2,
947
+ title: copy.step2.title,
948
+ visual: /* @__PURE__ */ jsxs5(
949
+ "div",
950
+ {
951
+ style: {
952
+ display: "flex",
953
+ alignItems: "center",
954
+ gap: 10,
955
+ background: "#f5f5f7",
956
+ borderRadius: 12,
957
+ padding: "12px 14px",
958
+ marginTop: 8
959
+ },
960
+ children: [
961
+ /* @__PURE__ */ jsx11(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
962
+ /* @__PURE__ */ jsx11("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
963
+ ]
964
+ }
965
+ )
966
+ }
967
+ ),
968
+ /* @__PURE__ */ jsx11(
969
+ Step2,
970
+ {
971
+ n: 3,
972
+ title: copy.step3.title,
973
+ visual: /* @__PURE__ */ jsx11(
974
+ "div",
975
+ {
976
+ style: {
977
+ display: "flex",
978
+ justifyContent: "flex-end",
979
+ background: "#f5f5f7",
980
+ borderRadius: 12,
981
+ padding: "10px 14px",
982
+ marginTop: 8
983
+ },
984
+ children: /* @__PURE__ */ jsx11(
985
+ "span",
986
+ {
987
+ style: {
988
+ color: "var(--hook-color-primary)",
989
+ fontSize: 15,
990
+ fontWeight: 600
991
+ },
992
+ children: copy.step3.buttonLabel
993
+ }
994
+ )
995
+ }
996
+ )
997
+ }
998
+ ),
999
+ /* @__PURE__ */ jsx11(
1000
+ "button",
1001
+ {
1002
+ "data-testid": "install-prompt-skip-session",
1003
+ type: "button",
1004
+ onClick: actions.dismissSession,
1005
+ style: { ...skipLinkStyle, marginTop: 16 },
1006
+ children: copy.skip
1007
+ }
1008
+ ),
1009
+ showPermanent && /* @__PURE__ */ jsx11(
1010
+ "button",
1011
+ {
1012
+ "data-testid": "install-prompt-skip-permanent",
1013
+ type: "button",
1014
+ onClick: actions.dismissPermanent,
1015
+ style: skipPermanentLinkStyle,
1016
+ children: copy.skipPermanent
1017
+ }
1018
+ )
1019
+ ] });
1020
+ }
1021
+ function Step2({
1022
+ n,
1023
+ title,
1024
+ subtitle,
1025
+ visual
1026
+ }) {
1027
+ return /* @__PURE__ */ jsxs5(
1028
+ "div",
1029
+ {
1030
+ style: {
1031
+ display: "flex",
1032
+ alignItems: "flex-start",
1033
+ gap: 12,
1034
+ marginBottom: 16,
1035
+ textAlign: "left"
1036
+ },
1037
+ children: [
1038
+ /* @__PURE__ */ jsx11(
1039
+ "div",
1040
+ {
1041
+ style: {
1042
+ width: 32,
1043
+ height: 32,
1044
+ borderRadius: 10,
1045
+ background: "var(--hook-color-primary)",
1046
+ color: "#fff",
1047
+ display: "flex",
1048
+ alignItems: "center",
1049
+ justifyContent: "center",
1050
+ fontSize: 15,
1051
+ fontWeight: 700,
1052
+ flexShrink: 0
1053
+ },
1054
+ children: n
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
1058
+ /* @__PURE__ */ jsx11("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
1059
+ subtitle && /* @__PURE__ */ jsx11("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
1060
+ visual
1061
+ ] })
1062
+ ]
1063
+ }
1064
+ );
1065
+ }
1066
+
1067
+ // src/components/InstallGate/variants/IOSOtherVariant.tsx
1068
+ import { useState as useState4 } from "react";
1069
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1070
+ function IOSOtherVariant({
1071
+ state,
1072
+ actions
1073
+ }) {
1074
+ const copy = INSTALL_COPY.iosOther;
1075
+ const showPermanent = shouldShowPermanentOption(state);
1076
+ const [copied, setCopied] = useState4(false);
1077
+ const handleCopy = async () => {
1078
+ await actions.copyLink();
1079
+ setCopied(true);
1080
+ setTimeout(() => setCopied(false), 2e3);
1081
+ };
1082
+ return /* @__PURE__ */ jsxs6(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
1083
+ /* @__PURE__ */ jsx12(
1084
+ "button",
1085
+ {
1086
+ "data-testid": "install-prompt-cta-ios-other-primary",
1087
+ type: "button",
1088
+ onClick: actions.redirectToSafari,
1089
+ style: primaryButtonStyle,
1090
+ children: copy.ctaPrimary
1091
+ }
1092
+ ),
1093
+ /* @__PURE__ */ jsx12(
1094
+ "button",
1095
+ {
1096
+ "data-testid": "install-prompt-cta-ios-other-secondary",
1097
+ type: "button",
1098
+ onClick: () => void handleCopy(),
1099
+ style: secondaryButtonStyle,
1100
+ children: copied ? copy.copiedToast : copy.ctaSecondary
1101
+ }
1102
+ ),
1103
+ /* @__PURE__ */ jsx12(
1104
+ "button",
1105
+ {
1106
+ "data-testid": "install-prompt-skip-session",
1107
+ type: "button",
1108
+ onClick: actions.dismissSession,
1109
+ style: skipLinkStyle,
1110
+ children: copy.skip
1111
+ }
1112
+ ),
1113
+ showPermanent && /* @__PURE__ */ jsx12(
1114
+ "button",
1115
+ {
1116
+ "data-testid": "install-prompt-skip-permanent",
1117
+ type: "button",
1118
+ onClick: actions.dismissPermanent,
1119
+ style: skipPermanentLinkStyle,
1120
+ children: copy.skipPermanent
1121
+ }
1122
+ )
1123
+ ] });
1124
+ }
1125
+
1126
+ // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1127
+ import { useState as useState5 } from "react";
1128
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1129
+ function InAppBrowserVariant({
1130
+ state,
1131
+ actions
1132
+ }) {
1133
+ const app = state.inAppApp ?? "other";
1134
+ const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1135
+ const copy = INSTALL_COPY.inApp;
1136
+ const showPermanent = shouldShowPermanentOption(state);
1137
+ const [copied, setCopied] = useState5(false);
1138
+ const handleCopy = async () => {
1139
+ await actions.copyLink();
1140
+ setCopied(true);
1141
+ setTimeout(() => setCopied(false), 2e3);
1142
+ };
1143
+ const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
1144
+ return /* @__PURE__ */ jsxs7(InstallSplash, { title: appCopy.title, children: [
1145
+ /* @__PURE__ */ jsx13(Step3, { n: 1, icon: /* @__PURE__ */ jsx13(DotsIcon, { size: 20 }), children: appCopy.step1 }),
1146
+ /* @__PURE__ */ jsx13(Step3, { n: 2, icon: /* @__PURE__ */ jsx13(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
1147
+ /* @__PURE__ */ jsx13(
1148
+ "button",
1149
+ {
1150
+ "data-testid": "install-prompt-cta-inapp-copy",
1151
+ type: "button",
1152
+ onClick: () => void handleCopy(),
1153
+ style: { ...primaryButtonStyle, marginTop: 8 },
1154
+ children: copied ? copy.copiedToast : copy.copy
1155
+ }
1156
+ ),
1157
+ /* @__PURE__ */ jsx13(
1158
+ "button",
1159
+ {
1160
+ "data-testid": "install-prompt-skip-session",
1161
+ type: "button",
1162
+ onClick: actions.dismissSession,
1163
+ style: skipLinkStyle,
1164
+ children: copy.skip
1165
+ }
1166
+ ),
1167
+ showPermanent && /* @__PURE__ */ jsx13(
1168
+ "button",
1169
+ {
1170
+ "data-testid": "install-prompt-skip-permanent",
1171
+ type: "button",
1172
+ onClick: actions.dismissPermanent,
1173
+ style: skipPermanentLinkStyle,
1174
+ children: copy.skipPermanent
1175
+ }
1176
+ )
1177
+ ] });
1178
+ }
1179
+ function Step3({
1180
+ n,
1181
+ icon,
1182
+ children
1183
+ }) {
1184
+ return /* @__PURE__ */ jsxs7(
1185
+ "div",
1186
+ {
1187
+ style: {
1188
+ display: "flex",
1189
+ alignItems: "center",
1190
+ gap: 12,
1191
+ padding: "12px 14px",
1192
+ background: "#f5f5f7",
1193
+ borderRadius: 12,
1194
+ marginBottom: 10,
1195
+ textAlign: "left"
1196
+ },
1197
+ children: [
1198
+ /* @__PURE__ */ jsx13(
1199
+ "div",
1200
+ {
1201
+ style: {
1202
+ width: 28,
1203
+ height: 28,
1204
+ borderRadius: "50%",
1205
+ background: "var(--hook-color-primary)",
1206
+ color: "#fff",
1207
+ display: "flex",
1208
+ alignItems: "center",
1209
+ justifyContent: "center",
1210
+ fontSize: 14,
1211
+ fontWeight: 700,
1212
+ flexShrink: 0
1213
+ },
1214
+ children: n
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsx13("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
1218
+ /* @__PURE__ */ jsx13("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
1219
+ ]
1220
+ }
1221
+ );
1222
+ }
1223
+
1224
+ // src/components/InstallGate/variants/DesktopVariant.tsx
1225
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1226
+ function DesktopVariant({
1227
+ state,
1228
+ actions
1229
+ }) {
1230
+ const { name, theme } = useTemplateConfig();
1231
+ const copy = INSTALL_COPY.desktop;
1232
+ const iconUrl = theme.icon_url || theme.logo_url || null;
1233
+ if (!state.isInstallable) return null;
1234
+ return /* @__PURE__ */ jsxs8(
1235
+ "div",
1236
+ {
1237
+ role: "complementary",
1238
+ "aria-label": copy.title,
1239
+ style: bannerStyle,
1240
+ children: [
1241
+ iconUrl ? /* @__PURE__ */ jsx14(
1242
+ "img",
1243
+ {
1244
+ src: iconUrl,
1245
+ alt: "",
1246
+ style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
1247
+ }
1248
+ ) : /* @__PURE__ */ jsx14(
1249
+ "div",
1250
+ {
1251
+ style: {
1252
+ width: 40,
1253
+ height: 40,
1254
+ borderRadius: 10,
1255
+ background: "var(--hook-color-primary)",
1256
+ color: "#fff",
1257
+ display: "flex",
1258
+ alignItems: "center",
1259
+ justifyContent: "center",
1260
+ fontSize: 18,
1261
+ fontWeight: 700,
1262
+ flexShrink: 0
1263
+ },
1264
+ children: name.charAt(0).toUpperCase()
1265
+ }
1266
+ ),
1267
+ /* @__PURE__ */ jsxs8("div", { style: { flex: 1, minWidth: 0 }, children: [
1268
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
1269
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxs8(
1272
+ "button",
1273
+ {
1274
+ "data-testid": "install-prompt-cta-desktop",
1275
+ type: "button",
1276
+ onClick: () => void actions.promptInstall(),
1277
+ style: {
1278
+ padding: "8px 14px",
1279
+ background: "var(--hook-color-primary)",
1280
+ color: "#fff",
1281
+ border: "none",
1282
+ borderRadius: 999,
1283
+ fontSize: 13,
1284
+ fontWeight: 600,
1285
+ cursor: "pointer",
1286
+ display: "inline-flex",
1287
+ alignItems: "center",
1288
+ gap: 6,
1289
+ flexShrink: 0
1290
+ },
1291
+ children: [
1292
+ /* @__PURE__ */ jsx14(DownloadIcon, { size: 14 }),
1293
+ copy.cta
1294
+ ]
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsx14(
1298
+ "button",
1299
+ {
1300
+ "data-testid": "install-prompt-desktop-close",
1301
+ type: "button",
1302
+ onClick: actions.dismissPermanent,
1303
+ "aria-label": copy.close,
1304
+ style: {
1305
+ background: "transparent",
1306
+ border: "none",
1307
+ cursor: "pointer",
1308
+ color: "#888",
1309
+ padding: 4,
1310
+ flexShrink: 0
1311
+ },
1312
+ children: /* @__PURE__ */ jsx14(XIcon, { size: 16 })
1313
+ }
1314
+ )
1315
+ ]
1316
+ }
1317
+ );
1318
+ }
1319
+ var bannerStyle = {
1320
+ position: "fixed",
1321
+ bottom: 24,
1322
+ right: 24,
1323
+ zIndex: 1e4,
1324
+ display: "flex",
1325
+ alignItems: "center",
1326
+ gap: 12,
1327
+ padding: "12px 16px",
1328
+ background: "#fff",
1329
+ border: "1px solid rgba(0,0,0,0.08)",
1330
+ borderRadius: 16,
1331
+ boxShadow: "0 10px 30px rgba(0,0,0,0.12)",
1332
+ maxWidth: 400
1333
+ };
1334
+
1335
+ // src/components/InstallGate/InstallGate.tsx
1336
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1337
+ function InstallGate({ children }) {
1338
+ const { slug, features_enabled } = useTemplateConfig();
1339
+ const enabled = features_enabled.includes("install_prompt");
1340
+ const installState = useInstallPrompt(slug);
1341
+ const shouldBlock = enabled && shouldBlockInstall(installState);
1342
+ const trackedRef = useRef(null);
1343
+ useEffect4(() => {
1344
+ if (!shouldBlock) return;
1345
+ if (typeof window === "undefined") return;
1346
+ const variantKey = `${slug}:${installState.variant}`;
1347
+ if (trackedRef.current === variantKey) return;
1348
+ trackedRef.current = variantKey;
1349
+ window.posthog?.capture?.("pwa_install_splash_shown", {
1350
+ slug,
1351
+ platform: installState.platform,
1352
+ browser: installState.iosBrowser ?? installState.androidBrowser ?? null,
1353
+ in_app_app: installState.inAppApp,
1354
+ variant: installState.variant
1355
+ });
1356
+ }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
1357
+ if (!enabled) return /* @__PURE__ */ jsx15(Fragment4, { children });
1358
+ if (installState.isInstalled) return /* @__PURE__ */ jsx15(Fragment4, { children });
1359
+ if (installState.variant === "desktop") {
1360
+ return /* @__PURE__ */ jsxs9(Fragment4, { children: [
1361
+ children,
1362
+ /* @__PURE__ */ jsx15(DesktopVariant, { state: installState, actions: installState })
1363
+ ] });
1364
+ }
1365
+ if (!shouldBlock) return /* @__PURE__ */ jsx15(Fragment4, { children });
1366
+ switch (installState.variant) {
1367
+ case "android-native":
1368
+ return /* @__PURE__ */ jsx15(AndroidNativeVariant, { state: installState, actions: installState });
1369
+ case "android-manual":
1370
+ return /* @__PURE__ */ jsx15(AndroidManualVariant, { state: installState, actions: installState });
1371
+ case "ios-safari":
1372
+ return /* @__PURE__ */ jsx15(IOSafariVariant, { state: installState, actions: installState });
1373
+ case "ios-other":
1374
+ return /* @__PURE__ */ jsx15(IOSOtherVariant, { state: installState, actions: installState });
1375
+ case "in-app":
1376
+ return /* @__PURE__ */ jsx15(InAppBrowserVariant, { state: installState, actions: installState });
1377
+ case "none":
1378
+ default:
1379
+ return /* @__PURE__ */ jsx15(Fragment4, { children });
1380
+ }
1381
+ }
1382
+
167
1383
  // src/defaults/ErrorBoundary.tsx
168
1384
  import { Component } from "react";
169
- import { Fragment as Fragment4, jsx as jsx7, jsxs } from "react/jsx-runtime";
1385
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
170
1386
  var ErrorBoundary = class extends Component {
171
1387
  state = { error: null };
172
1388
  static getDerivedStateFromError(error) {
@@ -177,17 +1393,17 @@ var ErrorBoundary = class extends Component {
177
1393
  }
178
1394
  render() {
179
1395
  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." })
1396
+ return this.props.fallback ?? /* @__PURE__ */ jsxs10("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
1397
+ /* @__PURE__ */ jsx16("h2", { children: "Algo deu errado" }),
1398
+ /* @__PURE__ */ jsx16("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
183
1399
  ] });
184
1400
  }
185
- return /* @__PURE__ */ jsx7(Fragment4, { children: this.props.children });
1401
+ return /* @__PURE__ */ jsx16(Fragment5, { children: this.props.children });
186
1402
  }
187
1403
  };
188
1404
 
189
1405
  // src/hooks/useLoginForm.ts
190
- import { useCallback as useCallback2, useMemo as useMemo2, useState as useState3 } from "react";
1406
+ import { useCallback as useCallback3, useMemo as useMemo2, useState as useState6 } from "react";
191
1407
  import { useHook as useHook4 } from "@hook-sdk/sdk";
192
1408
 
193
1409
  // src/errors.ts
@@ -224,10 +1440,10 @@ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
224
1440
  var MIN_PASSWORD = 8;
225
1441
  function useLoginForm() {
226
1442
  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);
1443
+ const [email, setEmail] = useState6("");
1444
+ const [password, setPassword] = useState6("");
1445
+ const [submitting, setSubmitting] = useState6(false);
1446
+ const [error, setError] = useState6(null);
231
1447
  const emailError = useMemo2(() => {
232
1448
  if (email.length === 0) return null;
233
1449
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -239,7 +1455,7 @@ function useLoginForm() {
239
1455
  return null;
240
1456
  }, [password]);
241
1457
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && emailError === null && passwordError === null && !submitting;
242
- const submit = useCallback2(async () => {
1458
+ const submit = useCallback3(async () => {
243
1459
  if (!canSubmit) return false;
244
1460
  setSubmitting(true);
245
1461
  setError(null);
@@ -263,25 +1479,183 @@ function useLoginForm() {
263
1479
  submit,
264
1480
  submitting,
265
1481
  canSubmit,
266
- error
1482
+ error,
1483
+ loginWithGoogle: () => auth.loginWithGoogle()
267
1484
  };
268
1485
  }
269
1486
 
1487
+ // src/internal/GoogleSignInButton.tsx
1488
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
1489
+ function GoogleSignInButton({
1490
+ onClick,
1491
+ testId = "oauth-google",
1492
+ label = "Continuar com Google"
1493
+ }) {
1494
+ return /* @__PURE__ */ jsxs11(
1495
+ "button",
1496
+ {
1497
+ "data-testid": testId,
1498
+ type: "button",
1499
+ onClick,
1500
+ style: {
1501
+ width: "100%",
1502
+ padding: "10px 12px",
1503
+ display: "flex",
1504
+ alignItems: "center",
1505
+ justifyContent: "center",
1506
+ gap: 10,
1507
+ background: "#fff",
1508
+ color: "#1f1f1f",
1509
+ border: "1px solid #dadce0",
1510
+ borderRadius: 8,
1511
+ cursor: "pointer",
1512
+ fontSize: 14,
1513
+ fontWeight: 500,
1514
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1515
+ },
1516
+ children: [
1517
+ /* @__PURE__ */ jsx17(GoogleGlyph, {}),
1518
+ label
1519
+ ]
1520
+ }
1521
+ );
1522
+ }
1523
+ function GoogleGlyph() {
1524
+ return /* @__PURE__ */ jsxs11("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
1525
+ /* @__PURE__ */ jsx17(
1526
+ "path",
1527
+ {
1528
+ 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",
1529
+ fill: "#4285F4"
1530
+ }
1531
+ ),
1532
+ /* @__PURE__ */ jsx17(
1533
+ "path",
1534
+ {
1535
+ 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",
1536
+ fill: "#34A853"
1537
+ }
1538
+ ),
1539
+ /* @__PURE__ */ jsx17(
1540
+ "path",
1541
+ {
1542
+ 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",
1543
+ fill: "#FBBC05"
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ jsx17(
1547
+ "path",
1548
+ {
1549
+ 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",
1550
+ fill: "#EA4335"
1551
+ }
1552
+ )
1553
+ ] });
1554
+ }
1555
+
1556
+ // src/internal/OAuthErrorBanner.tsx
1557
+ import { useEffect as useEffect5, useState as useState7 } from "react";
1558
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1559
+ var ERROR_MESSAGES = {
1560
+ invalid_state: "Sess\xE3o expirou, tente de novo.",
1561
+ access_denied: "Voc\xEA cancelou o login com Google.",
1562
+ provider_error: "O Google recusou a autentica\xE7\xE3o. Tente de novo em alguns segundos.",
1563
+ invalid_return_to: "Link inv\xE1lido. Tente entrar novamente."
1564
+ };
1565
+ function readErrorCode() {
1566
+ if (typeof window === "undefined") return null;
1567
+ const code = new URLSearchParams(window.location.search).get("oauth_error");
1568
+ if (!code) return null;
1569
+ return code;
1570
+ }
1571
+ function stripErrorFromUrl() {
1572
+ if (typeof window === "undefined") return;
1573
+ const url = new URL(window.location.href);
1574
+ url.searchParams.delete("oauth_error");
1575
+ window.history.replaceState({}, "", url.toString());
1576
+ }
1577
+ function OAuthErrorBanner() {
1578
+ const [code, setCode] = useState7(() => readErrorCode());
1579
+ useEffect5(() => {
1580
+ if (code !== null) stripErrorFromUrl();
1581
+ }, [code]);
1582
+ if (!code) return null;
1583
+ const message = ERROR_MESSAGES[code] ?? "N\xE3o conseguimos conectar ao Google. Tente de novo.";
1584
+ return /* @__PURE__ */ jsxs12(
1585
+ "div",
1586
+ {
1587
+ role: "alert",
1588
+ "data-testid": "oauth-error-banner",
1589
+ style: {
1590
+ padding: "10px 12px",
1591
+ marginBottom: 16,
1592
+ background: "#fce8e6",
1593
+ color: "#a50e0e",
1594
+ borderRadius: 8,
1595
+ fontSize: 14
1596
+ },
1597
+ children: [
1598
+ message,
1599
+ /* @__PURE__ */ jsx18(
1600
+ "button",
1601
+ {
1602
+ type: "button",
1603
+ onClick: () => setCode(null),
1604
+ "aria-label": "Fechar",
1605
+ style: {
1606
+ float: "right",
1607
+ background: "none",
1608
+ border: "none",
1609
+ color: "#a50e0e",
1610
+ cursor: "pointer",
1611
+ fontSize: 16,
1612
+ lineHeight: 1,
1613
+ padding: 0
1614
+ },
1615
+ children: "\xD7"
1616
+ }
1617
+ )
1618
+ ]
1619
+ }
1620
+ );
1621
+ }
1622
+
270
1623
  // src/defaults/DefaultLoginScreen.tsx
271
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
1624
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
272
1625
  function DefaultLoginScreen({ onNavigate }) {
273
1626
  const { name } = useTemplateConfig();
274
1627
  const f = useLoginForm();
275
- return /* @__PURE__ */ jsxs2("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
276
- /* @__PURE__ */ jsx8("h1", { style: { marginBottom: 8 }, children: name }),
277
- /* @__PURE__ */ jsx8("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
278
- /* @__PURE__ */ jsxs2("form", { onSubmit: (e) => {
1628
+ return /* @__PURE__ */ jsxs13("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1629
+ /* @__PURE__ */ jsx19("h1", { style: { marginBottom: 8 }, children: name }),
1630
+ /* @__PURE__ */ jsx19("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
1631
+ /* @__PURE__ */ jsx19(OAuthErrorBanner, {}),
1632
+ /* @__PURE__ */ jsx19(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
1633
+ /* @__PURE__ */ jsxs13(
1634
+ "div",
1635
+ {
1636
+ "aria-hidden": "true",
1637
+ style: {
1638
+ display: "flex",
1639
+ alignItems: "center",
1640
+ gap: 8,
1641
+ margin: "16px 0",
1642
+ color: "rgba(0,0,0,0.45)",
1643
+ fontSize: 12
1644
+ },
1645
+ children: [
1646
+ /* @__PURE__ */ jsx19("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1647
+ "ou",
1648
+ /* @__PURE__ */ jsx19("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1649
+ ]
1650
+ }
1651
+ ),
1652
+ /* @__PURE__ */ jsxs13("form", { onSubmit: (e) => {
279
1653
  e.preventDefault();
280
1654
  void f.submit();
281
1655
  }, children: [
282
- /* @__PURE__ */ jsxs2("label", { style: { display: "block", marginBottom: 12 }, children: [
1656
+ /* @__PURE__ */ jsxs13("label", { style: { display: "block", marginBottom: 12 }, children: [
283
1657
  "E-mail",
284
- /* @__PURE__ */ jsx8(
1658
+ /* @__PURE__ */ jsx19(
285
1659
  "input",
286
1660
  {
287
1661
  "data-testid": "login-email",
@@ -291,11 +1665,11 @@ function DefaultLoginScreen({ onNavigate }) {
291
1665
  style: { display: "block", width: "100%" }
292
1666
  }
293
1667
  ),
294
- f.emailError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.emailError })
1668
+ f.emailError && /* @__PURE__ */ jsx19("small", { style: { color: "#c00" }, children: f.emailError })
295
1669
  ] }),
296
- /* @__PURE__ */ jsxs2("label", { style: { display: "block", marginBottom: 12 }, children: [
1670
+ /* @__PURE__ */ jsxs13("label", { style: { display: "block", marginBottom: 12 }, children: [
297
1671
  "Senha",
298
- /* @__PURE__ */ jsx8(
1672
+ /* @__PURE__ */ jsx19(
299
1673
  "input",
300
1674
  {
301
1675
  "data-testid": "login-password",
@@ -305,10 +1679,10 @@ function DefaultLoginScreen({ onNavigate }) {
305
1679
  style: { display: "block", width: "100%" }
306
1680
  }
307
1681
  ),
308
- f.passwordError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.passwordError })
1682
+ f.passwordError && /* @__PURE__ */ jsx19("small", { style: { color: "#c00" }, children: f.passwordError })
309
1683
  ] }),
310
- f.error && /* @__PURE__ */ jsx8("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
311
- /* @__PURE__ */ jsx8(
1684
+ f.error && /* @__PURE__ */ jsx19("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1685
+ /* @__PURE__ */ jsx19(
312
1686
  "button",
313
1687
  {
314
1688
  "data-testid": "login-submit",
@@ -327,25 +1701,25 @@ function DefaultLoginScreen({ onNavigate }) {
327
1701
  }
328
1702
  )
329
1703
  ] }),
330
- /* @__PURE__ */ jsxs2("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
331
- /* @__PURE__ */ jsx8("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
332
- /* @__PURE__ */ jsx8("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
1704
+ /* @__PURE__ */ jsxs13("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
1705
+ /* @__PURE__ */ jsx19("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
1706
+ /* @__PURE__ */ jsx19("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
333
1707
  ] })
334
1708
  ] });
335
1709
  }
336
1710
 
337
1711
  // src/hooks/useSignupForm.ts
338
- import { useCallback as useCallback3, useMemo as useMemo3, useState as useState4 } from "react";
1712
+ import { useCallback as useCallback4, useMemo as useMemo3, useState as useState8 } from "react";
339
1713
  import { useHook as useHook5 } from "@hook-sdk/sdk";
340
1714
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
341
1715
  var MIN_PASSWORD2 = 8;
342
1716
  function useSignupForm() {
343
1717
  const { auth } = useHook5();
344
- const [name, setName] = useState4("");
345
- const [email, setEmail] = useState4("");
346
- const [password, setPassword] = useState4("");
347
- const [submitting, setSubmitting] = useState4(false);
348
- const [error, setError] = useState4(null);
1718
+ const [name, setName] = useState8("");
1719
+ const [email, setEmail] = useState8("");
1720
+ const [password, setPassword] = useState8("");
1721
+ const [submitting, setSubmitting] = useState8(false);
1722
+ const [error, setError] = useState8(null);
349
1723
  const nameError = useMemo3(() => {
350
1724
  if (name.length === 0) return null;
351
1725
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -362,7 +1736,7 @@ function useSignupForm() {
362
1736
  return null;
363
1737
  }, [password]);
364
1738
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
365
- const submit = useCallback3(async () => {
1739
+ const submit = useCallback4(async () => {
366
1740
  if (!canSubmit) return false;
367
1741
  setSubmitting(true);
368
1742
  setError(null);
@@ -389,61 +1763,83 @@ function useSignupForm() {
389
1763
  submit,
390
1764
  submitting,
391
1765
  canSubmit,
392
- error
1766
+ error,
1767
+ loginWithGoogle: () => auth.loginWithGoogle()
393
1768
  };
394
1769
  }
395
1770
 
396
1771
  // src/defaults/DefaultSignupScreen.tsx
397
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
1772
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
398
1773
  function DefaultSignupScreen({ onNavigate }) {
399
1774
  const { name } = useTemplateConfig();
400
1775
  const f = useSignupForm();
401
- return /* @__PURE__ */ jsxs3("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
402
- /* @__PURE__ */ jsx9("h1", { style: { marginBottom: 8 }, children: name }),
403
- /* @__PURE__ */ jsx9("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
404
- /* @__PURE__ */ jsxs3("form", { onSubmit: (e) => {
1776
+ return /* @__PURE__ */ jsxs14("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1777
+ /* @__PURE__ */ jsx20("h1", { style: { marginBottom: 8 }, children: name }),
1778
+ /* @__PURE__ */ jsx20("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
1779
+ /* @__PURE__ */ jsx20(OAuthErrorBanner, {}),
1780
+ /* @__PURE__ */ jsx20(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
1781
+ /* @__PURE__ */ jsxs14(
1782
+ "div",
1783
+ {
1784
+ "aria-hidden": "true",
1785
+ style: {
1786
+ display: "flex",
1787
+ alignItems: "center",
1788
+ gap: 8,
1789
+ margin: "16px 0",
1790
+ color: "rgba(0,0,0,0.45)",
1791
+ fontSize: 12
1792
+ },
1793
+ children: [
1794
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1795
+ "ou",
1796
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1797
+ ]
1798
+ }
1799
+ ),
1800
+ /* @__PURE__ */ jsxs14("form", { onSubmit: (e) => {
405
1801
  e.preventDefault();
406
1802
  void f.submit();
407
1803
  }, children: [
408
- /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
1804
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
409
1805
  "Nome",
410
- /* @__PURE__ */ jsx9("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
411
- f.nameError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.nameError })
1806
+ /* @__PURE__ */ jsx20("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
1807
+ f.nameError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.nameError })
412
1808
  ] }),
413
- /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
1809
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
414
1810
  "E-mail",
415
- /* @__PURE__ */ jsx9("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
416
- f.emailError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.emailError })
1811
+ /* @__PURE__ */ jsx20("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1812
+ f.emailError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.emailError })
417
1813
  ] }),
418
- /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
1814
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
419
1815
  "Senha",
420
- /* @__PURE__ */ jsx9("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
421
- f.passwordError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.passwordError })
1816
+ /* @__PURE__ */ jsx20("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
1817
+ f.passwordError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.passwordError })
422
1818
  ] }),
423
- f.error && /* @__PURE__ */ jsx9("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
424
- /* @__PURE__ */ jsx9("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" })
1819
+ f.error && /* @__PURE__ */ jsx20("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1820
+ /* @__PURE__ */ jsx20("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" })
425
1821
  ] }),
426
- /* @__PURE__ */ jsx9("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx9("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
1822
+ /* @__PURE__ */ jsx20("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx20("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
427
1823
  ] });
428
1824
  }
429
1825
 
430
1826
  // src/hooks/useForgotForm.ts
431
- import { useCallback as useCallback4, useMemo as useMemo4, useState as useState5 } from "react";
1827
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState9 } from "react";
432
1828
  import { useHook as useHook6 } from "@hook-sdk/sdk";
433
1829
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
434
1830
  function useForgotForm() {
435
1831
  const { auth } = useHook6();
436
- const [email, setEmail] = useState5("");
437
- const [submitting, setSubmitting] = useState5(false);
438
- const [sent, setSent] = useState5(false);
439
- const [error, setError] = useState5(null);
1832
+ const [email, setEmail] = useState9("");
1833
+ const [submitting, setSubmitting] = useState9(false);
1834
+ const [sent, setSent] = useState9(false);
1835
+ const [error, setError] = useState9(null);
440
1836
  const emailError = useMemo4(() => {
441
1837
  if (email.length === 0) return null;
442
1838
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
443
1839
  return null;
444
1840
  }, [email]);
445
1841
  const canSubmit = email.length > 0 && emailError === null && !submitting;
446
- const submit = useCallback4(async () => {
1842
+ const submit = useCallback5(async () => {
447
1843
  if (!canSubmit) return false;
448
1844
  setSubmitting(true);
449
1845
  setError(null);
@@ -471,49 +1867,49 @@ function useForgotForm() {
471
1867
  }
472
1868
 
473
1869
  // src/defaults/DefaultForgotScreen.tsx
474
- import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
1870
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
475
1871
  function DefaultForgotScreen({ onNavigate }) {
476
1872
  const { name } = useTemplateConfig();
477
1873
  const f = useForgotForm();
478
1874
  if (f.sent) {
479
- return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
480
- /* @__PURE__ */ jsx10("h1", { children: "Verifique seu e-mail" }),
481
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
482
- /* @__PURE__ */ jsx10("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
1875
+ return /* @__PURE__ */ jsxs15("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
1876
+ /* @__PURE__ */ jsx21("h1", { children: "Verifique seu e-mail" }),
1877
+ /* @__PURE__ */ jsx21("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
1878
+ /* @__PURE__ */ jsx21("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
483
1879
  ] });
484
1880
  }
485
- return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
486
- /* @__PURE__ */ jsx10("h1", { style: { marginBottom: 8 }, children: name }),
487
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
488
- /* @__PURE__ */ jsxs4("form", { onSubmit: (e) => {
1881
+ return /* @__PURE__ */ jsxs15("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1882
+ /* @__PURE__ */ jsx21("h1", { style: { marginBottom: 8 }, children: name }),
1883
+ /* @__PURE__ */ jsx21("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
1884
+ /* @__PURE__ */ jsxs15("form", { onSubmit: (e) => {
489
1885
  e.preventDefault();
490
1886
  void f.submit();
491
1887
  }, children: [
492
- /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
1888
+ /* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
493
1889
  "E-mail",
494
- /* @__PURE__ */ jsx10("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
495
- f.emailError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.emailError })
1890
+ /* @__PURE__ */ jsx21("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
1891
+ f.emailError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.emailError })
496
1892
  ] }),
497
- f.error && /* @__PURE__ */ jsx10("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
498
- /* @__PURE__ */ jsx10("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" })
1893
+ f.error && /* @__PURE__ */ jsx21("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1894
+ /* @__PURE__ */ jsx21("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" })
499
1895
  ] }),
500
- /* @__PURE__ */ jsx10("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx10("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
1896
+ /* @__PURE__ */ jsx21("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx21("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
501
1897
  ] });
502
1898
  }
503
1899
 
504
1900
  // src/hooks/useResetForm.ts
505
- import { useCallback as useCallback5, useEffect as useEffect3, useMemo as useMemo5, useState as useState6 } from "react";
1901
+ import { useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo5, useState as useState10 } from "react";
506
1902
  import { useHook as useHook7 } from "@hook-sdk/sdk";
507
1903
  var MIN_PASSWORD3 = 12;
508
1904
  function useResetForm() {
509
1905
  const { auth } = useHook7();
510
- const [token, setToken] = useState6(null);
511
- const [password, setPassword] = useState6("");
512
- const [confirm, setConfirm] = useState6("");
513
- const [submitting, setSubmitting] = useState6(false);
514
- const [done, setDone] = useState6(false);
515
- const [error, setError] = useState6(null);
516
- useEffect3(() => {
1906
+ const [token, setToken] = useState10(null);
1907
+ const [password, setPassword] = useState10("");
1908
+ const [confirm, setConfirm] = useState10("");
1909
+ const [submitting, setSubmitting] = useState10(false);
1910
+ const [done, setDone] = useState10(false);
1911
+ const [error, setError] = useState10(null);
1912
+ useEffect6(() => {
517
1913
  if (typeof window === "undefined") return;
518
1914
  const params = new URLSearchParams(window.location.search);
519
1915
  const t = params.get("token");
@@ -530,7 +1926,7 @@ function useResetForm() {
530
1926
  return null;
531
1927
  }, [confirm, password]);
532
1928
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
533
- const submit = useCallback5(async () => {
1929
+ const submit = useCallback6(async () => {
534
1930
  if (!canSubmit || token === null) return;
535
1931
  setSubmitting(true);
536
1932
  setError(null);
@@ -566,67 +1962,67 @@ function useResetForm() {
566
1962
  }
567
1963
 
568
1964
  // src/defaults/DefaultResetScreen.tsx
569
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1965
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
570
1966
  function DefaultResetScreen({ onNavigate }) {
571
1967
  const { name } = useTemplateConfig();
572
1968
  const f = useResetForm();
573
1969
  if (f.done) {
574
- return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
575
- /* @__PURE__ */ jsx11("h1", { children: "Senha alterada" }),
576
- /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
577
- /* @__PURE__ */ jsx11("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
1970
+ return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
1971
+ /* @__PURE__ */ jsx22("h1", { children: "Senha alterada" }),
1972
+ /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
1973
+ /* @__PURE__ */ jsx22("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
578
1974
  ] });
579
1975
  }
580
1976
  if (f.token === null) {
581
- return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
582
- /* @__PURE__ */ jsx11("h1", { children: "Link inv\xE1lido" }),
583
- /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
584
- /* @__PURE__ */ jsx11("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
1977
+ return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
1978
+ /* @__PURE__ */ jsx22("h1", { children: "Link inv\xE1lido" }),
1979
+ /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
1980
+ /* @__PURE__ */ jsx22("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
585
1981
  ] });
586
1982
  }
587
- return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
588
- /* @__PURE__ */ jsx11("h1", { style: { marginBottom: 8 }, children: name }),
589
- /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
590
- /* @__PURE__ */ jsxs5("form", { onSubmit: (e) => {
1983
+ return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
1984
+ /* @__PURE__ */ jsx22("h1", { style: { marginBottom: 8 }, children: name }),
1985
+ /* @__PURE__ */ jsx22("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
1986
+ /* @__PURE__ */ jsxs16("form", { onSubmit: (e) => {
591
1987
  e.preventDefault();
592
1988
  void f.submit();
593
1989
  }, children: [
594
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1990
+ /* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
595
1991
  "Nova senha",
596
- /* @__PURE__ */ jsx11("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" }),
597
- f.passwordError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.passwordError })
1992
+ /* @__PURE__ */ jsx22("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" }),
1993
+ f.passwordError && /* @__PURE__ */ jsx22("small", { style: { color: "#c00" }, children: f.passwordError })
598
1994
  ] }),
599
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1995
+ /* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
600
1996
  "Confirmar senha",
601
- /* @__PURE__ */ jsx11("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" }),
602
- f.confirmError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.confirmError })
1997
+ /* @__PURE__ */ jsx22("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" }),
1998
+ f.confirmError && /* @__PURE__ */ jsx22("small", { style: { color: "#c00" }, children: f.confirmError })
603
1999
  ] }),
604
- f.error && /* @__PURE__ */ jsx11("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
605
- /* @__PURE__ */ jsx11("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" })
2000
+ f.error && /* @__PURE__ */ jsx22("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
2001
+ /* @__PURE__ */ jsx22("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" })
606
2002
  ] })
607
2003
  ] });
608
2004
  }
609
2005
 
610
2006
  // src/defaults/DefaultPaywall.tsx
611
- import { useState as useState7 } from "react";
612
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
2007
+ import { useState as useState11 } from "react";
2008
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
613
2009
  function DefaultPaywall() {
614
2010
  const config = useTemplateConfig();
615
2011
  const { checkout, opening, error } = usePaywallState();
616
2012
  const p = config.subscription.paywall_config;
617
- const [cpf, setCpf] = useState7("");
2013
+ const [cpf, setCpf] = useState11("");
618
2014
  const cpfDigits = cpf.replace(/\D/g, "");
619
2015
  const canCheckout = cpfDigits.length === 11 && !opening;
620
- return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
621
- /* @__PURE__ */ jsx12("h1", { style: { marginBottom: 8 }, children: p.title }),
622
- p.subtitle && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
623
- /* @__PURE__ */ jsx12("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs6("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
624
- /* @__PURE__ */ jsx12("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
625
- /* @__PURE__ */ jsx12("span", { children: b })
2016
+ return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
2017
+ /* @__PURE__ */ jsx23("h1", { style: { marginBottom: 8 }, children: p.title }),
2018
+ p.subtitle && /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
2019
+ /* @__PURE__ */ jsx23("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs17("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
2020
+ /* @__PURE__ */ jsx23("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
2021
+ /* @__PURE__ */ jsx23("span", { children: b })
626
2022
  ] }, b)) }),
627
- /* @__PURE__ */ jsxs6("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
628
- /* @__PURE__ */ jsx12("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
629
- /* @__PURE__ */ jsx12(
2023
+ /* @__PURE__ */ jsxs17("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
2024
+ /* @__PURE__ */ jsx23("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
2025
+ /* @__PURE__ */ jsx23(
630
2026
  "input",
631
2027
  {
632
2028
  "data-testid": "paywall-cpf",
@@ -639,8 +2035,8 @@ function DefaultPaywall() {
639
2035
  }
640
2036
  )
641
2037
  ] }),
642
- error && /* @__PURE__ */ jsx12("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
643
- /* @__PURE__ */ jsx12(
2038
+ error && /* @__PURE__ */ jsx23("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
2039
+ /* @__PURE__ */ jsx23(
644
2040
  "button",
645
2041
  {
646
2042
  "data-testid": "paywall-cta",
@@ -661,21 +2057,21 @@ function DefaultPaywall() {
661
2057
  children: opening ? "Abrindo..." : p.cta
662
2058
  }
663
2059
  ),
664
- p.priceHint && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
665
- p.footerNote && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
2060
+ p.priceHint && /* @__PURE__ */ jsx23("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
2061
+ p.footerNote && /* @__PURE__ */ jsx23("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
666
2062
  ] });
667
2063
  }
668
2064
 
669
2065
  // src/AppRoot.tsx
670
- import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
2066
+ import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
671
2067
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
672
2068
  function PaymentReturnHandler({ children }) {
673
2069
  const { subscription } = useHook8();
674
- const subRef = useRef(subscription);
2070
+ const subRef = useRef2(subscription);
675
2071
  subRef.current = subscription;
676
- const runIdRef = useRef(0);
677
- const [state, setState] = useState8("idle");
678
- const runPoll = useCallback6(() => {
2072
+ const runIdRef = useRef2(0);
2073
+ const [state, setState] = useState12("idle");
2074
+ const runPoll = useCallback7(() => {
679
2075
  const runId = ++runIdRef.current;
680
2076
  setState("confirming");
681
2077
  let attempts = 0;
@@ -704,7 +2100,7 @@ function PaymentReturnHandler({ children }) {
704
2100
  };
705
2101
  void tick();
706
2102
  }, []);
707
- useEffect4(() => {
2103
+ useEffect7(() => {
708
2104
  if (typeof window === "undefined") return;
709
2105
  const url = new URL(window.location.href);
710
2106
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -714,20 +2110,20 @@ function PaymentReturnHandler({ children }) {
714
2110
  };
715
2111
  }, [runPoll]);
716
2112
  if (state === "confirming") {
717
- return /* @__PURE__ */ jsx13(
2113
+ return /* @__PURE__ */ jsx24(
718
2114
  "div",
719
2115
  {
720
2116
  role: "status",
721
2117
  "aria-live": "polite",
722
- style: overlayStyle,
2118
+ style: overlayStyle2,
723
2119
  children: "Confirmando pagamento\u2026"
724
2120
  }
725
2121
  );
726
2122
  }
727
2123
  if (state === "waiting") {
728
- return /* @__PURE__ */ jsx13("div", { role: "status", "aria-live": "polite", style: overlayStyle, children: /* @__PURE__ */ jsxs7("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
729
- /* @__PURE__ */ jsx13("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
730
- /* @__PURE__ */ jsx13(
2124
+ return /* @__PURE__ */ jsx24("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs18("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2125
+ /* @__PURE__ */ jsx24("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2126
+ /* @__PURE__ */ jsx24(
731
2127
  "button",
732
2128
  {
733
2129
  type: "button",
@@ -738,9 +2134,9 @@ function PaymentReturnHandler({ children }) {
738
2134
  )
739
2135
  ] }) });
740
2136
  }
741
- return /* @__PURE__ */ jsx13(Fragment5, { children });
2137
+ return /* @__PURE__ */ jsx24(Fragment6, { children });
742
2138
  }
743
- var overlayStyle = {
2139
+ var overlayStyle2 = {
744
2140
  position: "fixed",
745
2141
  inset: 0,
746
2142
  display: "flex",
@@ -771,14 +2167,14 @@ function AppRoot({
771
2167
  Reset = DefaultResetScreen,
772
2168
  Paywall = DefaultPaywall
773
2169
  }) {
774
- return /* @__PURE__ */ jsx13(PaymentReturnHandler, { children: /* @__PURE__ */ jsx13(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx13(ErrorBoundary, { children: /* @__PURE__ */ jsx13(ThemeProvider, { children: /* @__PURE__ */ jsx13(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx13(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs7(SubscriptionGate, { Paywall, children: [
2170
+ return /* @__PURE__ */ jsx24(PaymentReturnHandler, { children: /* @__PURE__ */ jsx24(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx24(ErrorBoundary, { children: /* @__PURE__ */ jsx24(ThemeProvider, { children: /* @__PURE__ */ jsx24(InstallGate, { children: /* @__PURE__ */ jsx24(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx24(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs18(SubscriptionGate, { Paywall, children: [
775
2171
  children,
776
- /* @__PURE__ */ jsx13(PushPrompt, {})
777
- ] }) }) }) }) }) }) });
2172
+ /* @__PURE__ */ jsx24(PushPrompt, {})
2173
+ ] }) }) }) }) }) }) }) });
778
2174
  }
779
2175
 
780
2176
  // src/hooks/usePush.ts
781
- import { useCallback as useCallback7, useEffect as useEffect5, useState as useState9 } from "react";
2177
+ import { useCallback as useCallback8, useEffect as useEffect8, useState as useState13 } from "react";
782
2178
  import { useHook as useHook9 } from "@hook-sdk/sdk";
783
2179
  function detectIosNeedsInstall() {
784
2180
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
@@ -806,11 +2202,11 @@ function deriveState(push) {
806
2202
  }
807
2203
  function usePush() {
808
2204
  const { push } = useHook9();
809
- const [state, setState] = useState9(() => deriveState(push));
810
- useEffect5(() => {
2205
+ const [state, setState] = useState13(() => deriveState(push));
2206
+ useEffect8(() => {
811
2207
  setState(deriveState(push));
812
2208
  }, [push]);
813
- const subscribe = useCallback7(async () => {
2209
+ const subscribe = useCallback8(async () => {
814
2210
  try {
815
2211
  await push.subscribe();
816
2212
  setState({ kind: "subscribed" });
@@ -822,7 +2218,7 @@ function usePush() {
822
2218
  throw e;
823
2219
  }
824
2220
  }, [push]);
825
- const unsubscribe = useCallback7(async () => {
2221
+ const unsubscribe = useCallback8(async () => {
826
2222
  try {
827
2223
  await push.unsubscribe();
828
2224
  setState({ kind: "prompt" });
@@ -835,31 +2231,31 @@ function usePush() {
835
2231
  }
836
2232
 
837
2233
  // src/components/PushPrompt.tsx
838
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
2234
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
839
2235
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
840
2236
  const { state, subscribe } = usePush();
841
2237
  if (state.kind === "subscribed") return null;
842
2238
  if (state.kind === "ios_needs_install") {
843
- return /* @__PURE__ */ jsxs8("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
844
- /* @__PURE__ */ jsx14("h3", { children: texts.iosInstallTitle }),
845
- /* @__PURE__ */ jsx14("p", { children: texts.iosInstallBody }),
846
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx14("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2239
+ return /* @__PURE__ */ jsxs19("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2240
+ /* @__PURE__ */ jsx25("h3", { children: texts.iosInstallTitle }),
2241
+ /* @__PURE__ */ jsx25("p", { children: texts.iosInstallBody }),
2242
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx25("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
847
2243
  ] });
848
2244
  }
849
2245
  if (state.kind === "denied") {
850
- return /* @__PURE__ */ jsxs8("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
851
- /* @__PURE__ */ jsx14("h3", { children: texts.deniedTitle }),
852
- /* @__PURE__ */ jsx14("p", { children: texts.deniedBody })
2246
+ return /* @__PURE__ */ jsxs19("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2247
+ /* @__PURE__ */ jsx25("h3", { children: texts.deniedTitle }),
2248
+ /* @__PURE__ */ jsx25("p", { children: texts.deniedBody })
853
2249
  ] });
854
2250
  }
855
2251
  if (state.kind === "unsupported") {
856
- return /* @__PURE__ */ jsx14("div", { className, role: "region", children: /* @__PURE__ */ jsx14("p", { children: texts.unsupportedBody }) });
2252
+ return /* @__PURE__ */ jsx25("div", { className, role: "region", children: /* @__PURE__ */ jsx25("p", { children: texts.unsupportedBody }) });
857
2253
  }
858
2254
  if (state.kind === "error") {
859
- return /* @__PURE__ */ jsx14("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx14("p", { children: state.message }) });
2255
+ return /* @__PURE__ */ jsx25("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx25("p", { children: state.message }) });
860
2256
  }
861
- return /* @__PURE__ */ jsxs8("div", { className, role: "region", children: [
862
- /* @__PURE__ */ jsx14(
2257
+ return /* @__PURE__ */ jsxs19("div", { className, role: "region", children: [
2258
+ /* @__PURE__ */ jsx25(
863
2259
  "button",
864
2260
  {
865
2261
  type: "button",
@@ -873,27 +2269,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
873
2269
  children: texts.cta
874
2270
  }
875
2271
  ),
876
- onDeclined && /* @__PURE__ */ jsx14("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2272
+ onDeclined && /* @__PURE__ */ jsx25("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
877
2273
  ] });
878
2274
  }
879
2275
 
880
2276
  // src/defaults/EmptyState.tsx
881
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
2277
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
882
2278
  function EmptyState({ title, description, action }) {
883
- return /* @__PURE__ */ jsxs9("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
884
- /* @__PURE__ */ jsx15("h2", { style: { marginBottom: 8 }, children: title }),
885
- description && /* @__PURE__ */ jsx15("p", { style: { opacity: 0.7 }, children: description }),
886
- action && /* @__PURE__ */ jsx15("div", { style: { marginTop: 16 }, children: action })
2279
+ return /* @__PURE__ */ jsxs20("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2280
+ /* @__PURE__ */ jsx26("h2", { style: { marginBottom: 8 }, children: title }),
2281
+ description && /* @__PURE__ */ jsx26("p", { style: { opacity: 0.7 }, children: description }),
2282
+ action && /* @__PURE__ */ jsx26("div", { style: { marginTop: 16 }, children: action })
887
2283
  ] });
888
2284
  }
889
2285
 
890
2286
  // src/hooks/useAuthPrimitives.ts
891
- import { useEffect as useEffect6 } from "react";
2287
+ import { useEffect as useEffect9 } from "react";
892
2288
  import { useHook as useHook10 } from "@hook-sdk/sdk";
893
2289
  var warned = false;
894
2290
  function useAuthPrimitives() {
895
2291
  const { auth } = useHook10();
896
- useEffect6(() => {
2292
+ useEffect9(() => {
897
2293
  if (!warned && process.env.NODE_ENV !== "production") {
898
2294
  warned = true;
899
2295
  console.warn(
@@ -924,14 +2320,14 @@ function useSubscription() {
924
2320
  }
925
2321
 
926
2322
  // src/hooks/useReminders.ts
927
- import { useCallback as useCallback8, useEffect as useEffect7, useState as useState10 } from "react";
2323
+ import { useCallback as useCallback9, useEffect as useEffect10, useState as useState14 } from "react";
928
2324
  import { useHook as useHook12 } from "@hook-sdk/sdk";
929
2325
  function useReminders() {
930
2326
  const { push } = useHook12();
931
2327
  const r = push.reminders;
932
- const [reminders, setReminders] = useState10([]);
933
- const [loading, setLoading] = useState10(true);
934
- const reload = useCallback8(async () => {
2328
+ const [reminders, setReminders] = useState14([]);
2329
+ const [loading, setLoading] = useState14(true);
2330
+ const reload = useCallback9(async () => {
935
2331
  setLoading(true);
936
2332
  try {
937
2333
  const next = await r.list();
@@ -940,38 +2336,38 @@ function useReminders() {
940
2336
  setLoading(false);
941
2337
  }
942
2338
  }, [r]);
943
- useEffect7(() => {
2339
+ useEffect10(() => {
944
2340
  void reload();
945
2341
  }, [reload]);
946
- const setReminder = useCallback8(async (input) => {
2342
+ const setReminder = useCallback9(async (input) => {
947
2343
  await r.set(input);
948
2344
  await reload();
949
2345
  }, [r, reload]);
950
- const deleteReminder = useCallback8(async (slot) => {
2346
+ const deleteReminder = useCallback9(async (slot) => {
951
2347
  await r.delete(slot);
952
2348
  await reload();
953
2349
  }, [r, reload]);
954
- const schedule = useCallback8(async (items) => {
2350
+ const schedule = useCallback9(async (items) => {
955
2351
  return r.schedule(items);
956
2352
  }, [r]);
957
- const setFallbacks = useCallback8(async (items) => {
2353
+ const setFallbacks = useCallback9(async (items) => {
958
2354
  return r.setFallbacks(items);
959
2355
  }, [r]);
960
2356
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
961
2357
  }
962
2358
 
963
2359
  // src/hooks/useToast.ts
964
- import { useCallback as useCallback9, useState as useState11 } from "react";
2360
+ import { useCallback as useCallback10, useState as useState15 } from "react";
965
2361
  function useToast() {
966
- const [items, setItems] = useState11([]);
967
- const show = useCallback9((message, kind = "info") => {
2362
+ const [items, setItems] = useState15([]);
2363
+ const show = useCallback10((message, kind = "info") => {
968
2364
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
969
2365
  setItems((prev) => [...prev, { id, message, kind }]);
970
2366
  setTimeout(() => {
971
2367
  setItems((prev) => prev.filter((t) => t.id !== id));
972
2368
  }, 4e3);
973
2369
  }, []);
974
- const dismiss = useCallback9((id) => {
2370
+ const dismiss = useCallback10((id) => {
975
2371
  setItems((prev) => prev.filter((t) => t.id !== id));
976
2372
  }, []);
977
2373
  return { items, show, dismiss };
@@ -985,11 +2381,21 @@ export {
985
2381
  DefaultSignupScreen,
986
2382
  EmptyState,
987
2383
  ErrorBoundary,
2384
+ InstallGate,
2385
+ InstallSplash,
988
2386
  LoadingState,
989
2387
  PushPrompt2 as PushPrompt,
2388
+ detectAndroidBrowser,
2389
+ detectIOSBrowser,
2390
+ detectInAppApp,
2391
+ detectPlatform,
2392
+ detectStandalone,
2393
+ shouldBlockInstall,
2394
+ shouldShowPermanentOption,
990
2395
  useAuth,
991
2396
  useAuthPrimitives,
992
2397
  useForgotForm,
2398
+ useInstallPrompt,
993
2399
  useLoginForm,
994
2400
  usePaywallState,
995
2401
  usePush,