@hook-sdk/template 0.3.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 useEffect5, useRef, useState as useState9 } from "react";
2
+ import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef2, useState as useState12 } from "react";
3
3
  import { useHook as useHook8 } from "@hook-sdk/sdk";
4
4
 
5
5
  // src/internal/TemplateConfigContext.tsx
@@ -164,9 +164,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);
@@ -269,13 +1485,13 @@ function useLoginForm() {
269
1485
  }
270
1486
 
271
1487
  // src/internal/GoogleSignInButton.tsx
272
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
1488
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
273
1489
  function GoogleSignInButton({
274
1490
  onClick,
275
1491
  testId = "oauth-google",
276
1492
  label = "Continuar com Google"
277
1493
  }) {
278
- return /* @__PURE__ */ jsxs2(
1494
+ return /* @__PURE__ */ jsxs11(
279
1495
  "button",
280
1496
  {
281
1497
  "data-testid": testId,
@@ -298,36 +1514,36 @@ function GoogleSignInButton({
298
1514
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
299
1515
  },
300
1516
  children: [
301
- /* @__PURE__ */ jsx8(GoogleGlyph, {}),
1517
+ /* @__PURE__ */ jsx17(GoogleGlyph, {}),
302
1518
  label
303
1519
  ]
304
1520
  }
305
1521
  );
306
1522
  }
307
1523
  function GoogleGlyph() {
308
- return /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
309
- /* @__PURE__ */ jsx8(
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(
310
1526
  "path",
311
1527
  {
312
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",
313
1529
  fill: "#4285F4"
314
1530
  }
315
1531
  ),
316
- /* @__PURE__ */ jsx8(
1532
+ /* @__PURE__ */ jsx17(
317
1533
  "path",
318
1534
  {
319
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",
320
1536
  fill: "#34A853"
321
1537
  }
322
1538
  ),
323
- /* @__PURE__ */ jsx8(
1539
+ /* @__PURE__ */ jsx17(
324
1540
  "path",
325
1541
  {
326
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",
327
1543
  fill: "#FBBC05"
328
1544
  }
329
1545
  ),
330
- /* @__PURE__ */ jsx8(
1546
+ /* @__PURE__ */ jsx17(
331
1547
  "path",
332
1548
  {
333
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",
@@ -338,8 +1554,8 @@ function GoogleGlyph() {
338
1554
  }
339
1555
 
340
1556
  // src/internal/OAuthErrorBanner.tsx
341
- import { useEffect as useEffect3, useState as useState4 } from "react";
342
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
1557
+ import { useEffect as useEffect5, useState as useState7 } from "react";
1558
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
343
1559
  var ERROR_MESSAGES = {
344
1560
  invalid_state: "Sess\xE3o expirou, tente de novo.",
345
1561
  access_denied: "Voc\xEA cancelou o login com Google.",
@@ -359,13 +1575,13 @@ function stripErrorFromUrl() {
359
1575
  window.history.replaceState({}, "", url.toString());
360
1576
  }
361
1577
  function OAuthErrorBanner() {
362
- const [code, setCode] = useState4(() => readErrorCode());
363
- useEffect3(() => {
1578
+ const [code, setCode] = useState7(() => readErrorCode());
1579
+ useEffect5(() => {
364
1580
  if (code !== null) stripErrorFromUrl();
365
1581
  }, [code]);
366
1582
  if (!code) return null;
367
1583
  const message = ERROR_MESSAGES[code] ?? "N\xE3o conseguimos conectar ao Google. Tente de novo.";
368
- return /* @__PURE__ */ jsxs3(
1584
+ return /* @__PURE__ */ jsxs12(
369
1585
  "div",
370
1586
  {
371
1587
  role: "alert",
@@ -380,7 +1596,7 @@ function OAuthErrorBanner() {
380
1596
  },
381
1597
  children: [
382
1598
  message,
383
- /* @__PURE__ */ jsx9(
1599
+ /* @__PURE__ */ jsx18(
384
1600
  "button",
385
1601
  {
386
1602
  type: "button",
@@ -405,16 +1621,16 @@ function OAuthErrorBanner() {
405
1621
  }
406
1622
 
407
1623
  // src/defaults/DefaultLoginScreen.tsx
408
- import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
1624
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
409
1625
  function DefaultLoginScreen({ onNavigate }) {
410
1626
  const { name } = useTemplateConfig();
411
1627
  const f = useLoginForm();
412
- return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
413
- /* @__PURE__ */ jsx10("h1", { style: { marginBottom: 8 }, children: name }),
414
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
415
- /* @__PURE__ */ jsx10(OAuthErrorBanner, {}),
416
- /* @__PURE__ */ jsx10(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
417
- /* @__PURE__ */ jsxs4(
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(
418
1634
  "div",
419
1635
  {
420
1636
  "aria-hidden": "true",
@@ -427,19 +1643,19 @@ function DefaultLoginScreen({ onNavigate }) {
427
1643
  fontSize: 12
428
1644
  },
429
1645
  children: [
430
- /* @__PURE__ */ jsx10("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1646
+ /* @__PURE__ */ jsx19("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
431
1647
  "ou",
432
- /* @__PURE__ */ jsx10("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1648
+ /* @__PURE__ */ jsx19("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
433
1649
  ]
434
1650
  }
435
1651
  ),
436
- /* @__PURE__ */ jsxs4("form", { onSubmit: (e) => {
1652
+ /* @__PURE__ */ jsxs13("form", { onSubmit: (e) => {
437
1653
  e.preventDefault();
438
1654
  void f.submit();
439
1655
  }, children: [
440
- /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
1656
+ /* @__PURE__ */ jsxs13("label", { style: { display: "block", marginBottom: 12 }, children: [
441
1657
  "E-mail",
442
- /* @__PURE__ */ jsx10(
1658
+ /* @__PURE__ */ jsx19(
443
1659
  "input",
444
1660
  {
445
1661
  "data-testid": "login-email",
@@ -449,11 +1665,11 @@ function DefaultLoginScreen({ onNavigate }) {
449
1665
  style: { display: "block", width: "100%" }
450
1666
  }
451
1667
  ),
452
- f.emailError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.emailError })
1668
+ f.emailError && /* @__PURE__ */ jsx19("small", { style: { color: "#c00" }, children: f.emailError })
453
1669
  ] }),
454
- /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
1670
+ /* @__PURE__ */ jsxs13("label", { style: { display: "block", marginBottom: 12 }, children: [
455
1671
  "Senha",
456
- /* @__PURE__ */ jsx10(
1672
+ /* @__PURE__ */ jsx19(
457
1673
  "input",
458
1674
  {
459
1675
  "data-testid": "login-password",
@@ -463,10 +1679,10 @@ function DefaultLoginScreen({ onNavigate }) {
463
1679
  style: { display: "block", width: "100%" }
464
1680
  }
465
1681
  ),
466
- f.passwordError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.passwordError })
1682
+ f.passwordError && /* @__PURE__ */ jsx19("small", { style: { color: "#c00" }, children: f.passwordError })
467
1683
  ] }),
468
- f.error && /* @__PURE__ */ jsx10("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
469
- /* @__PURE__ */ jsx10(
1684
+ f.error && /* @__PURE__ */ jsx19("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
1685
+ /* @__PURE__ */ jsx19(
470
1686
  "button",
471
1687
  {
472
1688
  "data-testid": "login-submit",
@@ -485,25 +1701,25 @@ function DefaultLoginScreen({ onNavigate }) {
485
1701
  }
486
1702
  )
487
1703
  ] }),
488
- /* @__PURE__ */ jsxs4("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
489
- /* @__PURE__ */ jsx10("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
490
- /* @__PURE__ */ jsx10("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
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" })
491
1707
  ] })
492
1708
  ] });
493
1709
  }
494
1710
 
495
1711
  // src/hooks/useSignupForm.ts
496
- import { useCallback as useCallback3, useMemo as useMemo3, useState as useState5 } from "react";
1712
+ import { useCallback as useCallback4, useMemo as useMemo3, useState as useState8 } from "react";
497
1713
  import { useHook as useHook5 } from "@hook-sdk/sdk";
498
1714
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
499
1715
  var MIN_PASSWORD2 = 8;
500
1716
  function useSignupForm() {
501
1717
  const { auth } = useHook5();
502
- const [name, setName] = useState5("");
503
- const [email, setEmail] = useState5("");
504
- const [password, setPassword] = useState5("");
505
- const [submitting, setSubmitting] = useState5(false);
506
- const [error, setError] = useState5(null);
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);
507
1723
  const nameError = useMemo3(() => {
508
1724
  if (name.length === 0) return null;
509
1725
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -520,7 +1736,7 @@ function useSignupForm() {
520
1736
  return null;
521
1737
  }, [password]);
522
1738
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
523
- const submit = useCallback3(async () => {
1739
+ const submit = useCallback4(async () => {
524
1740
  if (!canSubmit) return false;
525
1741
  setSubmitting(true);
526
1742
  setError(null);
@@ -553,16 +1769,16 @@ function useSignupForm() {
553
1769
  }
554
1770
 
555
1771
  // src/defaults/DefaultSignupScreen.tsx
556
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1772
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
557
1773
  function DefaultSignupScreen({ onNavigate }) {
558
1774
  const { name } = useTemplateConfig();
559
1775
  const f = useSignupForm();
560
- return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
561
- /* @__PURE__ */ jsx11("h1", { style: { marginBottom: 8 }, children: name }),
562
- /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
563
- /* @__PURE__ */ jsx11(OAuthErrorBanner, {}),
564
- /* @__PURE__ */ jsx11(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
565
- /* @__PURE__ */ jsxs5(
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(
566
1782
  "div",
567
1783
  {
568
1784
  "aria-hidden": "true",
@@ -575,55 +1791,55 @@ function DefaultSignupScreen({ onNavigate }) {
575
1791
  fontSize: 12
576
1792
  },
577
1793
  children: [
578
- /* @__PURE__ */ jsx11("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
1794
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
579
1795
  "ou",
580
- /* @__PURE__ */ jsx11("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
1796
+ /* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
581
1797
  ]
582
1798
  }
583
1799
  ),
584
- /* @__PURE__ */ jsxs5("form", { onSubmit: (e) => {
1800
+ /* @__PURE__ */ jsxs14("form", { onSubmit: (e) => {
585
1801
  e.preventDefault();
586
1802
  void f.submit();
587
1803
  }, children: [
588
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1804
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
589
1805
  "Nome",
590
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
591
- f.nameError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.nameError })
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 })
592
1808
  ] }),
593
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1809
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
594
1810
  "E-mail",
595
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
596
- f.emailError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.emailError })
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 })
597
1813
  ] }),
598
- /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
1814
+ /* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
599
1815
  "Senha",
600
- /* @__PURE__ */ jsx11("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
601
- f.passwordError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.passwordError })
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 })
602
1818
  ] }),
603
- f.error && /* @__PURE__ */ jsx11("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
604
- /* @__PURE__ */ jsx11("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
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" })
605
1821
  ] }),
606
- /* @__PURE__ */ jsx11("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx11("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
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" }) })
607
1823
  ] });
608
1824
  }
609
1825
 
610
1826
  // src/hooks/useForgotForm.ts
611
- import { useCallback as useCallback4, useMemo as useMemo4, useState as useState6 } from "react";
1827
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState9 } from "react";
612
1828
  import { useHook as useHook6 } from "@hook-sdk/sdk";
613
1829
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
614
1830
  function useForgotForm() {
615
1831
  const { auth } = useHook6();
616
- const [email, setEmail] = useState6("");
617
- const [submitting, setSubmitting] = useState6(false);
618
- const [sent, setSent] = useState6(false);
619
- const [error, setError] = useState6(null);
1832
+ const [email, setEmail] = useState9("");
1833
+ const [submitting, setSubmitting] = useState9(false);
1834
+ const [sent, setSent] = useState9(false);
1835
+ const [error, setError] = useState9(null);
620
1836
  const emailError = useMemo4(() => {
621
1837
  if (email.length === 0) return null;
622
1838
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
623
1839
  return null;
624
1840
  }, [email]);
625
1841
  const canSubmit = email.length > 0 && emailError === null && !submitting;
626
- const submit = useCallback4(async () => {
1842
+ const submit = useCallback5(async () => {
627
1843
  if (!canSubmit) return false;
628
1844
  setSubmitting(true);
629
1845
  setError(null);
@@ -651,49 +1867,49 @@ function useForgotForm() {
651
1867
  }
652
1868
 
653
1869
  // src/defaults/DefaultForgotScreen.tsx
654
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1870
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
655
1871
  function DefaultForgotScreen({ onNavigate }) {
656
1872
  const { name } = useTemplateConfig();
657
1873
  const f = useForgotForm();
658
1874
  if (f.sent) {
659
- return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
660
- /* @__PURE__ */ jsx12("h1", { children: "Verifique seu e-mail" }),
661
- /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
662
- /* @__PURE__ */ jsx12("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
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" })
663
1879
  ] });
664
1880
  }
665
- return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
666
- /* @__PURE__ */ jsx12("h1", { style: { marginBottom: 8 }, children: name }),
667
- /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
668
- /* @__PURE__ */ jsxs6("form", { onSubmit: (e) => {
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) => {
669
1885
  e.preventDefault();
670
1886
  void f.submit();
671
1887
  }, children: [
672
- /* @__PURE__ */ jsxs6("label", { style: { display: "block", marginBottom: 12 }, children: [
1888
+ /* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
673
1889
  "E-mail",
674
- /* @__PURE__ */ jsx12("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
675
- f.emailError && /* @__PURE__ */ jsx12("small", { style: { color: "#c00" }, children: f.emailError })
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 })
676
1892
  ] }),
677
- f.error && /* @__PURE__ */ jsx12("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
678
- /* @__PURE__ */ jsx12("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
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" })
679
1895
  ] }),
680
- /* @__PURE__ */ jsx12("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx12("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
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" }) })
681
1897
  ] });
682
1898
  }
683
1899
 
684
1900
  // src/hooks/useResetForm.ts
685
- import { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo5, useState as useState7 } from "react";
1901
+ import { useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo5, useState as useState10 } from "react";
686
1902
  import { useHook as useHook7 } from "@hook-sdk/sdk";
687
1903
  var MIN_PASSWORD3 = 12;
688
1904
  function useResetForm() {
689
1905
  const { auth } = useHook7();
690
- const [token, setToken] = useState7(null);
691
- const [password, setPassword] = useState7("");
692
- const [confirm, setConfirm] = useState7("");
693
- const [submitting, setSubmitting] = useState7(false);
694
- const [done, setDone] = useState7(false);
695
- const [error, setError] = useState7(null);
696
- useEffect4(() => {
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(() => {
697
1913
  if (typeof window === "undefined") return;
698
1914
  const params = new URLSearchParams(window.location.search);
699
1915
  const t = params.get("token");
@@ -710,7 +1926,7 @@ function useResetForm() {
710
1926
  return null;
711
1927
  }, [confirm, password]);
712
1928
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
713
- const submit = useCallback5(async () => {
1929
+ const submit = useCallback6(async () => {
714
1930
  if (!canSubmit || token === null) return;
715
1931
  setSubmitting(true);
716
1932
  setError(null);
@@ -746,67 +1962,67 @@ function useResetForm() {
746
1962
  }
747
1963
 
748
1964
  // src/defaults/DefaultResetScreen.tsx
749
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1965
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
750
1966
  function DefaultResetScreen({ onNavigate }) {
751
1967
  const { name } = useTemplateConfig();
752
1968
  const f = useResetForm();
753
1969
  if (f.done) {
754
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
755
- /* @__PURE__ */ jsx13("h1", { children: "Senha alterada" }),
756
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
757
- /* @__PURE__ */ jsx13("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
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" })
758
1974
  ] });
759
1975
  }
760
1976
  if (f.token === null) {
761
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
762
- /* @__PURE__ */ jsx13("h1", { children: "Link inv\xE1lido" }),
763
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
764
- /* @__PURE__ */ jsx13("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
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" })
765
1981
  ] });
766
1982
  }
767
- return /* @__PURE__ */ jsxs7("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
768
- /* @__PURE__ */ jsx13("h1", { style: { marginBottom: 8 }, children: name }),
769
- /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
770
- /* @__PURE__ */ jsxs7("form", { onSubmit: (e) => {
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) => {
771
1987
  e.preventDefault();
772
1988
  void f.submit();
773
1989
  }, children: [
774
- /* @__PURE__ */ jsxs7("label", { style: { display: "block", marginBottom: 12 }, children: [
1990
+ /* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
775
1991
  "Nova senha",
776
- /* @__PURE__ */ jsx13("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
777
- f.passwordError && /* @__PURE__ */ jsx13("small", { style: { color: "#c00" }, children: f.passwordError })
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 })
778
1994
  ] }),
779
- /* @__PURE__ */ jsxs7("label", { style: { display: "block", marginBottom: 12 }, children: [
1995
+ /* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
780
1996
  "Confirmar senha",
781
- /* @__PURE__ */ jsx13("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
782
- f.confirmError && /* @__PURE__ */ jsx13("small", { style: { color: "#c00" }, children: f.confirmError })
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 })
783
1999
  ] }),
784
- f.error && /* @__PURE__ */ jsx13("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
785
- /* @__PURE__ */ jsx13("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
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" })
786
2002
  ] })
787
2003
  ] });
788
2004
  }
789
2005
 
790
2006
  // src/defaults/DefaultPaywall.tsx
791
- import { useState as useState8 } from "react";
792
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
2007
+ import { useState as useState11 } from "react";
2008
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
793
2009
  function DefaultPaywall() {
794
2010
  const config = useTemplateConfig();
795
2011
  const { checkout, opening, error } = usePaywallState();
796
2012
  const p = config.subscription.paywall_config;
797
- const [cpf, setCpf] = useState8("");
2013
+ const [cpf, setCpf] = useState11("");
798
2014
  const cpfDigits = cpf.replace(/\D/g, "");
799
2015
  const canCheckout = cpfDigits.length === 11 && !opening;
800
- return /* @__PURE__ */ jsxs8("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
801
- /* @__PURE__ */ jsx14("h1", { style: { marginBottom: 8 }, children: p.title }),
802
- p.subtitle && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
803
- /* @__PURE__ */ jsx14("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs8("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
804
- /* @__PURE__ */ jsx14("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
805
- /* @__PURE__ */ jsx14("span", { children: b })
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 })
806
2022
  ] }, b)) }),
807
- /* @__PURE__ */ jsxs8("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
808
- /* @__PURE__ */ jsx14("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
809
- /* @__PURE__ */ jsx14(
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(
810
2026
  "input",
811
2027
  {
812
2028
  "data-testid": "paywall-cpf",
@@ -819,8 +2035,8 @@ function DefaultPaywall() {
819
2035
  }
820
2036
  )
821
2037
  ] }),
822
- error && /* @__PURE__ */ jsx14("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
823
- /* @__PURE__ */ jsx14(
2038
+ error && /* @__PURE__ */ jsx23("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
2039
+ /* @__PURE__ */ jsx23(
824
2040
  "button",
825
2041
  {
826
2042
  "data-testid": "paywall-cta",
@@ -841,21 +2057,21 @@ function DefaultPaywall() {
841
2057
  children: opening ? "Abrindo..." : p.cta
842
2058
  }
843
2059
  ),
844
- p.priceHint && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
845
- p.footerNote && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
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 })
846
2062
  ] });
847
2063
  }
848
2064
 
849
2065
  // src/AppRoot.tsx
850
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
2066
+ import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
851
2067
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
852
2068
  function PaymentReturnHandler({ children }) {
853
2069
  const { subscription } = useHook8();
854
- const subRef = useRef(subscription);
2070
+ const subRef = useRef2(subscription);
855
2071
  subRef.current = subscription;
856
- const runIdRef = useRef(0);
857
- const [state, setState] = useState9("idle");
858
- const runPoll = useCallback6(() => {
2072
+ const runIdRef = useRef2(0);
2073
+ const [state, setState] = useState12("idle");
2074
+ const runPoll = useCallback7(() => {
859
2075
  const runId = ++runIdRef.current;
860
2076
  setState("confirming");
861
2077
  let attempts = 0;
@@ -884,7 +2100,7 @@ function PaymentReturnHandler({ children }) {
884
2100
  };
885
2101
  void tick();
886
2102
  }, []);
887
- useEffect5(() => {
2103
+ useEffect7(() => {
888
2104
  if (typeof window === "undefined") return;
889
2105
  const url = new URL(window.location.href);
890
2106
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -894,20 +2110,20 @@ function PaymentReturnHandler({ children }) {
894
2110
  };
895
2111
  }, [runPoll]);
896
2112
  if (state === "confirming") {
897
- return /* @__PURE__ */ jsx15(
2113
+ return /* @__PURE__ */ jsx24(
898
2114
  "div",
899
2115
  {
900
2116
  role: "status",
901
2117
  "aria-live": "polite",
902
- style: overlayStyle,
2118
+ style: overlayStyle2,
903
2119
  children: "Confirmando pagamento\u2026"
904
2120
  }
905
2121
  );
906
2122
  }
907
2123
  if (state === "waiting") {
908
- return /* @__PURE__ */ jsx15("div", { role: "status", "aria-live": "polite", style: overlayStyle, children: /* @__PURE__ */ jsxs9("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
909
- /* @__PURE__ */ jsx15("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
910
- /* @__PURE__ */ jsx15(
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(
911
2127
  "button",
912
2128
  {
913
2129
  type: "button",
@@ -918,9 +2134,9 @@ function PaymentReturnHandler({ children }) {
918
2134
  )
919
2135
  ] }) });
920
2136
  }
921
- return /* @__PURE__ */ jsx15(Fragment5, { children });
2137
+ return /* @__PURE__ */ jsx24(Fragment6, { children });
922
2138
  }
923
- var overlayStyle = {
2139
+ var overlayStyle2 = {
924
2140
  position: "fixed",
925
2141
  inset: 0,
926
2142
  display: "flex",
@@ -951,14 +2167,14 @@ function AppRoot({
951
2167
  Reset = DefaultResetScreen,
952
2168
  Paywall = DefaultPaywall
953
2169
  }) {
954
- return /* @__PURE__ */ jsx15(PaymentReturnHandler, { children: /* @__PURE__ */ jsx15(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx15(ErrorBoundary, { children: /* @__PURE__ */ jsx15(ThemeProvider, { children: /* @__PURE__ */ jsx15(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx15(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs9(SubscriptionGate, { Paywall, children: [
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: [
955
2171
  children,
956
- /* @__PURE__ */ jsx15(PushPrompt, {})
957
- ] }) }) }) }) }) }) });
2172
+ /* @__PURE__ */ jsx24(PushPrompt, {})
2173
+ ] }) }) }) }) }) }) }) });
958
2174
  }
959
2175
 
960
2176
  // src/hooks/usePush.ts
961
- import { useCallback as useCallback7, useEffect as useEffect6, useState as useState10 } from "react";
2177
+ import { useCallback as useCallback8, useEffect as useEffect8, useState as useState13 } from "react";
962
2178
  import { useHook as useHook9 } from "@hook-sdk/sdk";
963
2179
  function detectIosNeedsInstall() {
964
2180
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
@@ -986,11 +2202,11 @@ function deriveState(push) {
986
2202
  }
987
2203
  function usePush() {
988
2204
  const { push } = useHook9();
989
- const [state, setState] = useState10(() => deriveState(push));
990
- useEffect6(() => {
2205
+ const [state, setState] = useState13(() => deriveState(push));
2206
+ useEffect8(() => {
991
2207
  setState(deriveState(push));
992
2208
  }, [push]);
993
- const subscribe = useCallback7(async () => {
2209
+ const subscribe = useCallback8(async () => {
994
2210
  try {
995
2211
  await push.subscribe();
996
2212
  setState({ kind: "subscribed" });
@@ -1002,7 +2218,7 @@ function usePush() {
1002
2218
  throw e;
1003
2219
  }
1004
2220
  }, [push]);
1005
- const unsubscribe = useCallback7(async () => {
2221
+ const unsubscribe = useCallback8(async () => {
1006
2222
  try {
1007
2223
  await push.unsubscribe();
1008
2224
  setState({ kind: "prompt" });
@@ -1015,31 +2231,31 @@ function usePush() {
1015
2231
  }
1016
2232
 
1017
2233
  // src/components/PushPrompt.tsx
1018
- import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
2234
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
1019
2235
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
1020
2236
  const { state, subscribe } = usePush();
1021
2237
  if (state.kind === "subscribed") return null;
1022
2238
  if (state.kind === "ios_needs_install") {
1023
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
1024
- /* @__PURE__ */ jsx16("h3", { children: texts.iosInstallTitle }),
1025
- /* @__PURE__ */ jsx16("p", { children: texts.iosInstallBody }),
1026
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx16("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
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 })
1027
2243
  ] });
1028
2244
  }
1029
2245
  if (state.kind === "denied") {
1030
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
1031
- /* @__PURE__ */ jsx16("h3", { children: texts.deniedTitle }),
1032
- /* @__PURE__ */ jsx16("p", { children: texts.deniedBody })
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 })
1033
2249
  ] });
1034
2250
  }
1035
2251
  if (state.kind === "unsupported") {
1036
- return /* @__PURE__ */ jsx16("div", { className, role: "region", children: /* @__PURE__ */ jsx16("p", { children: texts.unsupportedBody }) });
2252
+ return /* @__PURE__ */ jsx25("div", { className, role: "region", children: /* @__PURE__ */ jsx25("p", { children: texts.unsupportedBody }) });
1037
2253
  }
1038
2254
  if (state.kind === "error") {
1039
- return /* @__PURE__ */ jsx16("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx16("p", { children: state.message }) });
2255
+ return /* @__PURE__ */ jsx25("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx25("p", { children: state.message }) });
1040
2256
  }
1041
- return /* @__PURE__ */ jsxs10("div", { className, role: "region", children: [
1042
- /* @__PURE__ */ jsx16(
2257
+ return /* @__PURE__ */ jsxs19("div", { className, role: "region", children: [
2258
+ /* @__PURE__ */ jsx25(
1043
2259
  "button",
1044
2260
  {
1045
2261
  type: "button",
@@ -1053,27 +2269,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
1053
2269
  children: texts.cta
1054
2270
  }
1055
2271
  ),
1056
- onDeclined && /* @__PURE__ */ jsx16("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2272
+ onDeclined && /* @__PURE__ */ jsx25("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
1057
2273
  ] });
1058
2274
  }
1059
2275
 
1060
2276
  // src/defaults/EmptyState.tsx
1061
- import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
2277
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1062
2278
  function EmptyState({ title, description, action }) {
1063
- return /* @__PURE__ */ jsxs11("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
1064
- /* @__PURE__ */ jsx17("h2", { style: { marginBottom: 8 }, children: title }),
1065
- description && /* @__PURE__ */ jsx17("p", { style: { opacity: 0.7 }, children: description }),
1066
- action && /* @__PURE__ */ jsx17("div", { style: { marginTop: 16 }, children: action })
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 })
1067
2283
  ] });
1068
2284
  }
1069
2285
 
1070
2286
  // src/hooks/useAuthPrimitives.ts
1071
- import { useEffect as useEffect7 } from "react";
2287
+ import { useEffect as useEffect9 } from "react";
1072
2288
  import { useHook as useHook10 } from "@hook-sdk/sdk";
1073
2289
  var warned = false;
1074
2290
  function useAuthPrimitives() {
1075
2291
  const { auth } = useHook10();
1076
- useEffect7(() => {
2292
+ useEffect9(() => {
1077
2293
  if (!warned && process.env.NODE_ENV !== "production") {
1078
2294
  warned = true;
1079
2295
  console.warn(
@@ -1104,14 +2320,14 @@ function useSubscription() {
1104
2320
  }
1105
2321
 
1106
2322
  // src/hooks/useReminders.ts
1107
- import { useCallback as useCallback8, useEffect as useEffect8, useState as useState11 } from "react";
2323
+ import { useCallback as useCallback9, useEffect as useEffect10, useState as useState14 } from "react";
1108
2324
  import { useHook as useHook12 } from "@hook-sdk/sdk";
1109
2325
  function useReminders() {
1110
2326
  const { push } = useHook12();
1111
2327
  const r = push.reminders;
1112
- const [reminders, setReminders] = useState11([]);
1113
- const [loading, setLoading] = useState11(true);
1114
- const reload = useCallback8(async () => {
2328
+ const [reminders, setReminders] = useState14([]);
2329
+ const [loading, setLoading] = useState14(true);
2330
+ const reload = useCallback9(async () => {
1115
2331
  setLoading(true);
1116
2332
  try {
1117
2333
  const next = await r.list();
@@ -1120,38 +2336,38 @@ function useReminders() {
1120
2336
  setLoading(false);
1121
2337
  }
1122
2338
  }, [r]);
1123
- useEffect8(() => {
2339
+ useEffect10(() => {
1124
2340
  void reload();
1125
2341
  }, [reload]);
1126
- const setReminder = useCallback8(async (input) => {
2342
+ const setReminder = useCallback9(async (input) => {
1127
2343
  await r.set(input);
1128
2344
  await reload();
1129
2345
  }, [r, reload]);
1130
- const deleteReminder = useCallback8(async (slot) => {
2346
+ const deleteReminder = useCallback9(async (slot) => {
1131
2347
  await r.delete(slot);
1132
2348
  await reload();
1133
2349
  }, [r, reload]);
1134
- const schedule = useCallback8(async (items) => {
2350
+ const schedule = useCallback9(async (items) => {
1135
2351
  return r.schedule(items);
1136
2352
  }, [r]);
1137
- const setFallbacks = useCallback8(async (items) => {
2353
+ const setFallbacks = useCallback9(async (items) => {
1138
2354
  return r.setFallbacks(items);
1139
2355
  }, [r]);
1140
2356
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
1141
2357
  }
1142
2358
 
1143
2359
  // src/hooks/useToast.ts
1144
- import { useCallback as useCallback9, useState as useState12 } from "react";
2360
+ import { useCallback as useCallback10, useState as useState15 } from "react";
1145
2361
  function useToast() {
1146
- const [items, setItems] = useState12([]);
1147
- const show = useCallback9((message, kind = "info") => {
2362
+ const [items, setItems] = useState15([]);
2363
+ const show = useCallback10((message, kind = "info") => {
1148
2364
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1149
2365
  setItems((prev) => [...prev, { id, message, kind }]);
1150
2366
  setTimeout(() => {
1151
2367
  setItems((prev) => prev.filter((t) => t.id !== id));
1152
2368
  }, 4e3);
1153
2369
  }, []);
1154
- const dismiss = useCallback9((id) => {
2370
+ const dismiss = useCallback10((id) => {
1155
2371
  setItems((prev) => prev.filter((t) => t.id !== id));
1156
2372
  }, []);
1157
2373
  return { items, show, dismiss };
@@ -1165,11 +2381,21 @@ export {
1165
2381
  DefaultSignupScreen,
1166
2382
  EmptyState,
1167
2383
  ErrorBoundary,
2384
+ InstallGate,
2385
+ InstallSplash,
1168
2386
  LoadingState,
1169
2387
  PushPrompt2 as PushPrompt,
2388
+ detectAndroidBrowser,
2389
+ detectIOSBrowser,
2390
+ detectInAppApp,
2391
+ detectPlatform,
2392
+ detectStandalone,
2393
+ shouldBlockInstall,
2394
+ shouldShowPermanentOption,
1170
2395
  useAuth,
1171
2396
  useAuthPrimitives,
1172
2397
  useForgotForm,
2398
+ useInstallPrompt,
1173
2399
  useLoginForm,
1174
2400
  usePaywallState,
1175
2401
  usePush,